From 93bc65801860dab41d3ab42fb8f295a209006f57 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 25 Aug 2023 17:03:08 +0200 Subject: [PATCH 01/71] feat: add color mapping component --- .../categorical_color_mapping.tsx | 83 ++++++ .../color_mapping/color/apca.ts | 140 ++++++++++ .../color_mapping/color/color_handling.ts | 160 +++++++++++ .../color_mapping/color/color_math.ts | 38 +++ .../color_mapping/color/rule_matching.ts | 45 +++ .../components/assignment/assignment.tsx | 116 ++++++++ .../components/assignment/match.tsx | 82 ++++++ .../components/assignment/range.tsx | 67 +++++ .../assignment/special_assignment.tsx | 67 +++++ .../components/color_picker/color_picker.tsx | 84 ++++++ .../components/color_picker/color_swatch.tsx | 127 +++++++++ .../components/color_picker/hsl_picker.tsx | 101 +++++++ .../color_picker/palette_colors.tsx | 101 +++++++ .../components/color_picker/rgb_picker.tsx | 113 ++++++++ .../components/container/container.tsx | 201 ++++++++++++++ .../components/palette_selector/gradient.scss | 28 ++ .../components/palette_selector/gradient.tsx | 261 ++++++++++++++++++ .../palette_selector/palette_selector.tsx | 170 ++++++++++++ .../config/assignment_from_categories.ts | 65 +++++ .../color_mapping/config/assignments.ts | 91 ++++++ .../config/default_color_mapping.ts | 41 +++ .../color_mapping/config/index.ts | 9 + .../color_mapping/config/types.ts | 121 ++++++++ .../shared_components/color_mapping/index.ts | 15 + .../color_mapping/palette.ts | 21 ++ .../palettes/default_palettes.ts | 86 ++++++ .../color_mapping/palettes/eui.ts | 32 +++ .../color_mapping/palettes/ikea.ts | 10 + .../color_mapping/palettes/neutral.ts | 11 + .../color_mapping/palettes/pastel.ts | 28 ++ .../color_mapping/palettes/tableau.ts | 32 +++ .../color_mapping/state/color_mapping.ts | 210 ++++++++++++++ .../color_mapping/state/selectors.ts | 26 ++ .../color_mapping/state/ui.ts | 55 ++++ .../src/shared_components/index.ts | 2 + 35 files changed, 2839 insertions(+) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/range.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/hsl_picker.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/config/assignment_from_categories.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/config/index.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/index.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palette.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/state/ui.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx new file mode 100644 index 0000000000000..847d48a9fb835 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { Provider } from 'react-redux'; +import { type EnhancedStore, configureStore } from '@reduxjs/toolkit'; +import { isEqual } from 'lodash'; +import { colorMappingReducer, updateModel } from './state/color_mapping'; +import { Container } from './components/container/container'; +import { ColorMapping } from './config'; +import { uiReducer } from './state/ui'; + +export type ColorMappingInputData = + | { + type: 'categories'; + /** an ORDERED array of categories rendered in the visualization */ + categories: Array; + specialHandling: Map; + } + | { + type: 'ranges'; + min: number; + max: number; + bins: number; + }; + +export interface ColorMappingProps { + /** the initial color mapping model, usually coming from a the visualization saved object */ + model: ColorMapping.Config; + /** A map of paletteId and palette configuration */ + palettes: Map; + data: ColorMappingInputData; + isDarkMode: boolean; + + /** a function called at every change in the model */ + onModelUpdate: (model: ColorMapping.Config) => void; +} + +/** + * The React component for mapping categorical values to colors + */ +export class CategoricalColorMapping extends React.Component { + store: EnhancedStore<{ colorMapping: ColorMapping.Config }>; + unsubscribe: () => void; + constructor(props: ColorMappingProps) { + super(props); + // configure the store at mount time + this.store = configureStore({ + preloadedState: { + colorMapping: props.model, + }, + reducer: { + colorMapping: colorMappingReducer, + ui: uiReducer, + }, + }); + // subscribe to store changes to update external tools + this.unsubscribe = this.store.subscribe(() => { + this.props.onModelUpdate(this.store.getState().colorMapping); + }); + } + componentWillUnmount() { + this.unsubscribe(); + } + componentDidUpdate(prevProps: Readonly) { + if (!isEqual(prevProps.model, this.props.model)) { + this.store.dispatch(updateModel(this.props.model)); + } + } + render() { + const { palettes, data, isDarkMode } = this.props; + return ( + + + + ); + } +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts new file mode 100644 index 0000000000000..b1d3f5a2a812d --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @public */ +export type RGB = number; + +/** @public */ +export type A = number; + +/** @internal */ +export type RgbTuple = [r: RGB, g: RGB, b: RGB, alpha?: A]; + +/** @public */ +export type RgbaTuple = [r: RGB, g: RGB, b: RGB, alpha: A]; + +/** + * / // DO NOT use a Y from any other method ///// + * @internal + */ +export function APCAContrast([Rbg, Gbg, Bbg]: RgbTuple, [Rtxt, Gtxt, Btxt]: RgbTuple) { + // / // sRGB Conversion to Relative Luminance (Y) ///// + + const mainTRC = 2.4; // Transfer Curve (aka "Gamma") for sRGB linearization + // Simple power curve vs piecewise described in docs + // Essentially, 2.4 best models actual display + // characteristics in combination with the total method + + const Rco = 0.2126729; // sRGB Red Coefficient (from matrix) + const Gco = 0.7151522; // sRGB Green Coefficient (from matrix) + const Bco = 0.072175; // sRGB Blue Coefficient (from matrix) + + // / // For Finding Raw SAPC Contrast from Relative Luminance (Y) ///// + + const normBG = 0.56; // Constants for SAPC Power Curve Exponents + const normTXT = 0.57; // One pair for normal text, and one for reverse + const revTXT = 0.62; // These are the "beating heart" of SAPC + const revBG = 0.65; + + // / // For Clamping and Scaling Values ///// + // constant updated to https://github.com/Myndex/SAPC-APCA#apca-math-new-098g-4g-constants + // new 0.98G 4g constants + + const blkThrs = 0.022; // Level that triggers the soft black clamp + const blkClmp = 1.414; // Exponent for the soft black clamp curve + const deltaYmin = 0.0005; // Lint trap + const scaleBoW = 1.14; // Scaling for dark text on light + const scaleWoB = 1.14; // Scaling for light text on dark + const loConThresh = 0.035991; // Threshold for new simple offset scale + const loConFactor = 27.7847239587675; + const loConOffset = 0.027; // The simple offset + const loClip = 0.001; // Output clip (lint trap #2) + + // We are only concerned with Y at this point + // Ybg and Ytxt: divide sRGB to 0.0-1.0 range, linearize, + // and then apply the standard coefficients and sum to Y. + // Note that the Y we create here is unique and designed + // exclusively for SAPC. Do not use Y from other methods. + + let Ybg = + Math.pow(Rbg / 255.0, mainTRC) * Rco + + Math.pow(Gbg / 255.0, mainTRC) * Gco + + Math.pow(Bbg / 255.0, mainTRC) * Bco; + + let Ytxt = + Math.pow(Rtxt / 255.0, mainTRC) * Rco + + Math.pow(Gtxt / 255.0, mainTRC) * Gco + + Math.pow(Btxt / 255.0, mainTRC) * Bco; + + let SAPC = 0.0; // For holding raw SAPC values + let outputContrast = 0.0; // For weighted final values + + // / // TUTORIAL ///// + + // Take Y and soft clamp black, return 0 for very close luminances + // determine polarity, and calculate SAPC raw contrast + // Then apply the output scaling + + // Note that reverse contrast (white text on black) + // intentionally returns a negative number + // Proper polarity is important! + + // / /////// BLACK SOFT CLAMP & INPUT CLIP //////////////////////////////// + + // Soft clamp Y when near black. + // Now clamping all colors to prevent crossover errors + Ytxt = Ytxt > blkThrs ? Ytxt : Ytxt + Math.pow(blkThrs - Ytxt, blkClmp); + + Ybg = Ybg > blkThrs ? Ybg : Ybg + Math.pow(blkThrs - Ybg, blkClmp); + + // / // Return 0 Early for extremely low ∆Y (lint trap #1) ///// + if (Math.abs(Ybg - Ytxt) < deltaYmin) { + return 0.0; + } + + // / /////// SAPC CONTRAST /////////////////////////////////////////////// + + if (Ybg > Ytxt) { + // For normal polarity, black text on white + + // / // Calculate the SAPC contrast value and scale + + SAPC = (Math.pow(Ybg, normBG) - Math.pow(Ytxt, normTXT)) * scaleBoW; + + // / // NEW! SAPC SmoothScale™ + // Low Contrast Smooth Scale Rollout to prevent polarity reversal + // and also a low clip for very low contrasts (lint trap #2) + // much of this is for very low contrasts, less than 10 + // therefore for most reversing needs, only loConOffset is important + outputContrast = + SAPC < loClip + ? 0.0 + : SAPC < loConThresh + ? SAPC - SAPC * loConFactor * loConOffset + : SAPC - loConOffset; + } else { + // For reverse polarity, light text on dark + // WoB should always return negative value. + + SAPC = (Math.pow(Ybg, revBG) - Math.pow(Ytxt, revTXT)) * scaleWoB; + + outputContrast = + SAPC > -loClip + ? 0.0 + : SAPC > -loConThresh + ? SAPC - SAPC * loConFactor * loConOffset + : SAPC + loConOffset; + } + + return outputContrast * 100; +} // Close APCAcontrast() + +// / /\ ///////////////////////////////////////////\ +// / //\ END OF SAPC/APCA BLOCK /////////////////////////////////////////////\ +// / ///////////////////////////////////////////////////////////////////////////\ +// / ////////////////////////////////////////////////////////////////////////////\ diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts new file mode 100644 index 0000000000000..b3122617748a5 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { scaleSequential } from 'd3-scale'; +import { interpolateHsl, piecewise } from 'd3-interpolate'; +import { ColorMapping } from '../config'; +import { changeAlpha } from './color_math'; +import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; +import { getPalette } from '../palette'; +import { ColorMappingInputData } from '../categorical_color_mapping'; +import { ruleMatch } from './rule_matching'; + +export type Color = string; + +export function getAssignmentColor( + colorMode: ColorMapping.Config['colorMode'], + color: ColorMapping.Config['assignments'][number]['color'], + getPaletteFn: ReturnType, + isDarkMode: boolean, + index: number, + total: number +) { + switch (color.type) { + case 'colorCode': + case 'categorical': + return getColor(color, getPaletteFn, isDarkMode); + case 'gradient': { + if (colorMode.type === 'categorical') { + return 'red'; + } + const steps = + colorMode.steps.length === 1 + ? [ + // TODO: change lumisonity instead of alpha + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 1), + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + ] + : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); + steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); + const colorScale = scaleSequential(piecewise(interpolateHsl, steps)); + return colorScale(index / total); + } + } +} + +export function getColor( + color: ColorMapping.ColorCode | ColorMapping.CategoricalColor, + getPaletteFn: ReturnType, + isDarkMode: boolean +) { + switch (color.type) { + case 'colorCode': + return color.colorCode; + case 'categorical': + return getPaletteFn(color.paletteId).getColor(color.colorIndex, isDarkMode); + } +} + +export function getColorFactory( + model: ColorMapping.Config, + getPaletteFn: ReturnType, + isDarkMode: boolean, + data: ColorMappingInputData +): (category: string | number | string[]) => Color { + const palette = getPaletteFn(model.paletteId); + // generate on-the-fly assignments in auto-mode based on current data. + // This simplify the code by always using assignments, even if there is no real static assigmnets + const assignments = + model.assignmentMode === 'auto' + ? generateAutoAssignmentsForCategories(data, palette, model.colorMode) + : model.assignments; + + // find auto-assigned colors + const autoAssignedColors = + data.type === 'categories' + ? assignments.filter((a) => { + return ( + a.rule.type === 'auto' || (a.rule.type === 'matchExactly' && a.rule.values.length === 0) + ); + }) + : []; + + // find all categories that doesn't match with an assignment + const nonAssignedCategories = + data.type === 'categories' + ? data.categories.filter((category) => { + return !assignments.some(({ rule }) => ruleMatch(rule, category)); + }) + : []; + + // TODO: check if number type is needed here + return (category: string | number | string[]) => { + if (typeof category === 'string' || Array.isArray(category)) { + const nonAssignedCategoryIndex = nonAssignedCategories.indexOf(category); + + // return color for a non assigned category + if (nonAssignedCategoryIndex > -1) { + if (nonAssignedCategoryIndex < autoAssignedColors.length) { + const autoAssignmentIndex = assignments.findIndex( + (d) => d === autoAssignedColors[nonAssignedCategoryIndex] + ); + return getAssignmentColor( + model.colorMode, + autoAssignedColors[nonAssignedCategoryIndex].color, + getPaletteFn, + isDarkMode, + autoAssignmentIndex, + assignments.length - 1 + ); + } + // if no auto-assign color rule/color is available then use the other color + // TODO: the specialAssignment[0] position is arbitrary, we should fix it better + return getColor(model.specialAssignments[0].color, getPaletteFn, isDarkMode); + } + + // find the assignment where the category matches the rule + const matchingAssignmentIndex = assignments.findIndex(({ rule }) => { + return ruleMatch(rule, category); + }); + + // return the assigned color + if (matchingAssignmentIndex > -1) { + const assignment = assignments[matchingAssignmentIndex]; + return getAssignmentColor( + model.colorMode, + assignment.color, + getPaletteFn, + isDarkMode, + matchingAssignmentIndex, + assignments.length - 1 + ); + } + // if no assign color rule/color is available then use the other color + // TODO: the specialAssignment[0] position is arbitrary, we should fix it better + return getColor(model.specialAssignments[0].color, getPaletteFn, isDarkMode); + } else { + const matchingAssignmentIndex = assignments.findIndex(({ rule }) => { + return ruleMatch(rule, category); + }); + + if (matchingAssignmentIndex > -1) { + const assignment = assignments[matchingAssignmentIndex]; + return getAssignmentColor( + model.colorMode, + assignment.color, + getPaletteFn, + isDarkMode, + matchingAssignmentIndex, + assignments.length - 1 + ); + } + return getColor(model.specialAssignments[0].color, getPaletteFn, isDarkMode); + } + }; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts new file mode 100644 index 0000000000000..080bf09086f6b --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import chroma from 'chroma-js'; +import { APCAContrast } from './apca'; + +export function getValidColor(color: string): chroma.Color { + try { + return chroma(color); + } catch { + return chroma('red'); + } +} + +export function getColorContrast(color: string, isDark: boolean) { + const [r, g, b] = getValidColor(color).rgb(); + // TODO: change background depending on theme + const value = APCAContrast(isDark ? [0, 0, 0] : [255, 255, 255], [r, g, b]); + return { value: value.toFixed(1), contrast: Math.abs(value) > 40 }; +} + +export function changeAlpha(color: string, alpha: number) { + const [r, g, b] = getValidColor(color).rgb(); + return `rgba(${r},${g},${b}, ${alpha})`; +} + +export function toHex(color: string) { + return getValidColor(color).hex(); +} + +export function isSameColor(color1: string, color2: string) { + return toHex(color1) === toHex(color2); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts new file mode 100644 index 0000000000000..4b7a1630f88ab --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from '../config'; + +export function ruleMatch( + rule: ColorMapping.Config['assignments'][number]['rule'], + value: string | number | string[] +) { + switch (rule.type) { + case 'matchExactly': + if (Array.isArray(value)) { + return rule.values.some( + (v) => + Array.isArray(v) && v.length === value.length && v.every((part, i) => part === value[i]) + ); + } + return rule.values.includes(`${value}`); + case 'matchExactlyCI': + return rule.values.some((d) => d.toLowerCase() === `${value}`); + case 'range': + // TODO: color by value not yet possible in all charts in elastic-charts + return typeof value === 'number' ? rangeMatch(rule, value) : false; + default: + return false; + } +} + +export function rangeMatch(rule: ColorMapping.RuleRange, value: number) { + return ( + (rule.min === rule.max && rule.min === value) || + ((rule.minInclusive ? value >= rule.min : value > rule.min) && + (rule.maxInclusive ? value <= rule.max : value < rule.max)) + ); +} + +export const SPECIAL_RULE_MATCHES = new Map([ + ['__other__', 'Other'], + ['', '(empty)'], +]); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx new file mode 100644 index 0000000000000..ce98b94753a52 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { useDispatch } from 'react-redux'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + removeAssignment, + updateAssignmentColor, + updateAssignmentRule, +} from '../../state/color_mapping'; +import { ColorMapping } from '../../config'; +import { Range } from './range'; +import { Match } from './match'; +import { getPalette } from '../../palette'; + +import { ColorMappingInputData } from '../../categorical_color_mapping'; +import { ColorSwatch } from '../color_picker/color_swatch'; + +export function Assignment({ + data, + assignment, + disableDelete, + index, + total, + canPickColor, + palette, + colorMode, + getPaletteFn, + isDarkMode, +}: { + data: ColorMappingInputData; + index: number; + total: number; + colorMode: ColorMapping.Config['colorMode']; + assignment: ColorMapping.Config['assignments'][number]; + disableDelete: boolean; + palette: ColorMapping.CategoricalPalette; + getPaletteFn: ReturnType; + canPickColor: boolean; + isDarkMode: boolean; +}) { + const dispatch = useDispatch(); + + return ( + + + { + dispatch(updateAssignmentColor({ assignmentIndex: index, color })); + }} + /> + + + {assignment.rule.type === 'auto' || + assignment.rule.type === 'matchExactly' || + assignment.rule.type === 'matchExactlyCI' ? ( + ) => { + dispatch( + updateAssignmentRule({ + assignmentIndex: index, + rule: values.length === 0 ? { type: 'auto' } : { type: 'matchExactly', values }, + }) + ); + }} + /> + ) : assignment.rule.type === 'range' ? ( + { + const rule: ColorMapping.RuleRange = { + type: 'range', + min, + max, + minInclusive, + maxInclusive, + }; + dispatch(updateAssignmentRule({ assignmentIndex: index, rule })); + }} + /> + ) : null} + + + dispatch(removeAssignment(index))} + aria-label="Delete assignment" + /> + + + ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx new file mode 100644 index 0000000000000..c42c1333e3b54 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiComboBox, EuiFlexItem } from '@elastic/eui'; +import { ColorMapping } from '../../config'; +export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; + +export const Match: React.FC<{ + index: number; + editable: boolean; + rule: + | ColorMapping.RuleAuto + | ColorMapping.RuleMatchExactly + | ColorMapping.RuleMatchExactlyCI + | ColorMapping.RuleRegExp; + updateValue: (values: Array) => void; + options: Array; + specialHandling: Map; +}> = ({ rule, updateValue, editable, options, specialHandling }) => { + const selectedOptions = + rule.type === 'auto' + ? [] + : typeof rule.values === 'string' + ? [{ label: rule.values, value: rule.values }] + : rule.values.map((value) => { + const ruleValues = Array.isArray(value) ? value : [value]; + return { + label: ruleValues + .map((v) => specialHandling.get(v) ?? v) + .join(MULTI_FIELD_VALUES_SEPARATOR), + value, + }; + }); + + const convertedOptions = options.map((value) => { + const ruleValues = Array.isArray(value) ? value : [value]; + return { + label: ruleValues.map((v) => specialHandling.get(v) ?? v).join(MULTI_FIELD_VALUES_SEPARATOR), + value, + }; + }); + + return ( + + { + updateValue( + changedOptions.reduce>((acc, option) => { + if (option.value !== undefined) { + acc.push(option.value); + } + return acc; + }, []) + ); + }} + onCreateOption={(e) => { + const label = e.trim(); + if ( + selectedOptions.findIndex((option) => option.label.trim().toLowerCase() === label) === + -1 + ) { + updateValue([...selectedOptions, { label, value: label }].map((d) => d.value)); + } + }} + isClearable={false} + data-test-subj="demoComboBox" + /> + + ); +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/range.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/range.tsx new file mode 100644 index 0000000000000..70f2cf49609e0 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/range.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiButtonEmpty, EuiFieldNumber, EuiFlexItem } from '@elastic/eui'; +import { ColorMapping } from '../../config'; + +export const Range: React.FC<{ + rule: ColorMapping.RuleRange; + editable: boolean; + updateValue: (min: number, max: number, minInclusive: boolean, maxInclusive: boolean) => void; +}> = ({ rule, updateValue, editable }) => { + const minValid = rule.min <= rule.max; + const maxValid = rule.max >= rule.min; + + return ( + <> + + updateValue(rule.min, rule.max, !rule.minInclusive, rule.maxInclusive)} + > + {rule.minInclusive ? 'GTE' : 'GT'} + + } + placeholder="min" + value={rule.min} + isInvalid={!minValid} + disabled={!editable} + onChange={(e) => + updateValue(+e.currentTarget.value, rule.max, rule.minInclusive, rule.maxInclusive) + } + aria-label="The min value" + /> + + + updateValue(rule.min, rule.max, rule.minInclusive, !rule.maxInclusive)} + > + {rule.maxInclusive ? 'LTE' : 'LT'} + + } + placeholder="max" + disabled={!editable} + value={rule.max} + onChange={(e) => + updateValue(rule.min, +e.currentTarget.value, rule.minInclusive, rule.maxInclusive) + } + aria-label="The max value" + /> + + + ); +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx new file mode 100644 index 0000000000000..f304d7f93fab5 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useDispatch } from 'react-redux'; +import React from 'react'; +import { ColorMapping } from '../../config'; +import { getPalette } from '../../palette'; +import { ColorSwatch } from '../color_picker/color_swatch'; +import { updateSpecialAssignmentColor } from '../../state/color_mapping'; + +export function SpecialAssignment({ + assignment, + index, + palette, + getPaletteFn, + isDarkMode, +}: { + isDarkMode: boolean; + index: number; + assignment: ColorMapping.Config['specialAssignments'][number]; + palette: ColorMapping.CategoricalPalette; + getPaletteFn: ReturnType; +}) { + const dispatch = useDispatch(); + const canPickColor = true; + return ( + + + { + dispatch( + updateSpecialAssignmentColor({ + assignmentIndex: index, + color, + }) + ); + }} + /> + + + + + + ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx new file mode 100644 index 0000000000000..97464c849e6e1 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { EuiButton, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; +import { ColorMapping } from '../../config'; +import { NeutralPalette } from '../../palettes/default_palettes'; +import { getPalette } from '../../palette'; +import { PaletteColors } from './palette_colors'; +import { RGBPicker } from './rgb_picker'; + +export function ColorPicker({ + palette, + getPaletteFn, + color, + close, + selectColor, + isDarkMode, + deleteStep, +}: { + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode; + getPaletteFn: ReturnType; + palette: ColorMapping.CategoricalPalette; + isDarkMode: boolean; + close: () => void; + selectColor: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; + deleteStep?: () => void; +}) { + const [tab, setTab] = useState( + color.type === 'categorical' && + (color.paletteId === palette.id || color.paletteId === NeutralPalette.id) + ? 'palette' + : 'custom' + ); + + return ( +
+ + setTab('palette')} isSelected={tab === 'palette'}> + Palette + + setTab('custom')} isSelected={tab === 'custom'}> + Custom + + + + {tab === 'palette' ? ( + + ) : ( + + )} + {deleteStep ? ( + { + close(); + deleteStep(); + }} + aria-label="Delete color" + > + Remove from gradient + + ) : null} +
+ ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx new file mode 100644 index 0000000000000..bc3e6618fbec6 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiColorPickerSwatch, EuiPopover } from '@elastic/eui'; +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { ColorPicker } from './color_picker'; +import { getAssignmentColor } from '../../color/color_handling'; +import { ColorMapping } from '../../config'; +import { getPalette } from '../../palette'; +import { removeGradientColorStep } from '../../state/color_mapping'; + +import { getColorPickerVisibilitySelector } from '../../state/selectors'; +import { colorPickerVisibility, hideColorPickerVisibility } from '../../state/ui'; +interface ColorPickerSwatchProps { + colorMode: ColorMapping.Config['colorMode']; + assignmentColor: + | ColorMapping.Config['assignments'][number]['color'] + | ColorMapping.Config['specialAssignments'][number]['color']; + getPaletteFn: ReturnType; + canPickColor: boolean; + index: number; + total: number; + palette: ColorMapping.CategoricalPalette; + onColorChange: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; + swatchShape: 'square' | 'round'; + isDarkMode: boolean; + forType: 'assignment' | 'specialAssignment' | 'gradient'; +} +export const ColorSwatch = ({ + colorMode, + assignmentColor, + getPaletteFn, + canPickColor, + index, + total, + palette, + onColorChange, + swatchShape, + isDarkMode, + forType, +}: ColorPickerSwatchProps) => { + const colorPickerState = useSelector(getColorPickerVisibilitySelector); + const dispatch = useDispatch(); + const colorPickerVisible = + colorPickerState.index === index && + colorPickerState.type === forType && + colorPickerState.visibile; + const colorHex = getAssignmentColor( + colorMode, + assignmentColor, + getPaletteFn, + isDarkMode, + index, + total + ); + + return canPickColor && assignmentColor.type !== 'gradient' ? ( + dispatch(hideColorPickerVisibility())} + button={ + dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} + style={{ + ...(swatchShape === 'round' ? { borderRadius: '50%', width: 15, height: 15 } : {}), + // the color swatch doesn't work here... + backgroundColor: colorHex, + cursor: canPickColor ? 'pointer' : 'not-allowed', + }} + className={ + swatchShape === 'round' + ? 'colorMappingColorSwatchEditableRound' + : 'colorMappingColorSwatchEditable' + } + /> + } + > + dispatch(hideColorPickerVisibility())} + isDarkMode={isDarkMode} + selectColor={(color) => { + // dispatch update + onColorChange(color); + }} + deleteStep={ + colorMode.type === 'gradient' && index > 0 + ? () => dispatch(removeGradientColorStep(index)) + : undefined + } + /> + + ) : ( + + ); +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/hsl_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/hsl_picker.tsx new file mode 100644 index 0000000000000..3dcde03be5ea9 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/hsl_picker.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + EuiButton, + EuiColorPicker, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { ColorMapping } from '../../config'; + +import { getColorContrast } from '../../color/color_math'; +import { getPalette } from '../../palette'; + +export function HSLPicker({ + isDarkMode, + color, + getPaletteFn, + selectColor, + close, +}: { + palette: ColorMapping.CategoricalPalette; + isDarkMode: boolean; + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode; + getPaletteFn: ReturnType; + selectColor: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; + close: () => void; +}) { + const [customColor, setCustomColor] = useState< + ColorMapping.CategoricalColor | ColorMapping.ColorCode + >(color); + + const customColorHex = + customColor.type === 'categorical' + ? getPaletteFn(customColor.paletteId).getColor(customColor.colorIndex, isDarkMode) + : customColor.colorCode; + return ( + + + + Contrast:{' '} + {getColorContrast(customColorHex, isDarkMode).contrast ? ( + + ) : ( + + )} +
+ APCA: + {getColorContrast(customColorHex, isDarkMode).value} +
+
+ + { + setCustomColor({ + type: 'colorCode', + colorCode: c, + }); + }} + color={customColorHex} + display="inline" + swatches={[]} + showAlpha + /> + + + + { + if (color !== customColor) { + selectColor(customColor); + } else { + close(); + } + }} + > + Apply this color + + + + { + close(); + }} + > + Close + + +
+ ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx new file mode 100644 index 0000000000000..d5c61f4ed4613 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { EuiColorPickerSwatch, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { ColorMapping } from '../../config'; +import { NeutralPalette } from '../../palettes/default_palettes'; +import { isSameColor } from '../../color/color_math'; +import { getPalette } from '../../palette'; + +export function PaletteColors({ + palette, + isDarkMode, + color, + getPaletteFn, + selectColor, +}: { + palette: ColorMapping.CategoricalPalette; + isDarkMode: boolean; + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode; + getPaletteFn: ReturnType; + selectColor: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; +}) { + const colors = Array.from({ length: palette.colorCount }, (d, i) => { + return palette.getColor(i, isDarkMode); + }); + const neutralColors = Array.from({ length: NeutralPalette.colorCount }, (d, i) => { + return NeutralPalette.getColor(i, isDarkMode); + }); + const originalColor = + color.type === 'categorical' + ? color.paletteId === NeutralPalette.id + ? NeutralPalette.getColor(color.colorIndex, isDarkMode) + : getPaletteFn(color.paletteId).getColor(color.colorIndex, isDarkMode) + : color.colorCode; + return ( + + + + Palette + + + {colors.map((c, index) => ( + + + selectColor({ type: 'categorical', paletteId: palette.id, colorIndex: index }) + } + /> + + ))} + + + + + + Neutral + + + {neutralColors.map((c, index) => ( + + + selectColor({ + type: 'categorical', + paletteId: NeutralPalette.id, + colorIndex: index, + }) + } + /> + + ))} + + + + ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx new file mode 100644 index 0000000000000..608c91a9c1cb2 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + EuiButton, + EuiColorPicker, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { ColorMapping } from '../../config'; + +import { getColorContrast } from '../../color/color_math'; +import { getPalette } from '../../palette'; + +export function RGBPicker({ + isDarkMode, + color, + getPaletteFn, + selectColor, + close, +}: { + palette: ColorMapping.CategoricalPalette; + isDarkMode: boolean; + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode; + getPaletteFn: ReturnType; + selectColor: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; + close: () => void; +}) { + const [customColor, setCustomColor] = useState< + ColorMapping.CategoricalColor | ColorMapping.ColorCode + >(color); + + const customColorHex = + customColor.type === 'categorical' + ? getPaletteFn(customColor.paletteId).getColor(customColor.colorIndex, isDarkMode) + : customColor.colorCode; + + const lightContrast = getColorContrast(customColorHex, false); + const darkContrast = getColorContrast(customColorHex, true); + return ( + + + + Light Theme Contrast:{' '} + +
+ APCA: + {lightContrast.value} +
+ Dark Theme Contrast:{' '} + +
+ APCA: + {darkContrast.value} +
+
+
+ + { + setCustomColor({ + type: 'colorCode', + colorCode: c, + }); + }} + color={customColorHex} + display="inline" + swatches={[]} + showAlpha + /> + + + + { + if (color !== customColor) { + selectColor(customColor); + } else { + close(); + } + }} + > + Apply this color + + + + { + close(); + }} + > + Close + + +
+ ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx new file mode 100644 index 0000000000000..d5e59a16422a6 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiPanel, + EuiSpacer, + EuiSwitch, +} from '@elastic/eui'; +import { Assignment } from '../assignment/assignment'; +import { SpecialAssignment } from '../assignment/special_assignment'; +import { PaletteSelector } from '../palette_selector/palette_selector'; + +import { + RootState, + addNewAssignment, + assignAutomatically, + assignStatically, + changeGradientSortOrder, +} from '../../state/color_mapping'; +import { generateAutoAssignmentsForCategories } from '../../config/assignment_from_categories'; +import { ColorMapping } from '../../config'; +import { NeutralPalette } from '../../palettes/default_palettes'; +import { getUnusedColorForNewAssignment } from '../../config/assignments'; +import { getPalette } from '../../palette'; +import { + getColorModeSelector, + getPaletteSelector, + getSpecialAssignmentsSelector, + isAutoAssignmentModeSelector, +} from '../../state/selectors'; +import { ColorMappingInputData } from '../../categorical_color_mapping'; +import { Gradient } from '../palette_selector/gradient'; + +export const MAX_ASSIGNABLE_COLORS = 10; + +function getComputedAssignmentsSelector( + data: ColorMappingInputData, + palette: ColorMapping.CategoricalPalette, + colorMode: ColorMapping.Config['colorMode'] +) { + return (state: RootState) => + state.colorMapping.assignmentMode === 'auto' + ? generateAutoAssignmentsForCategories(data, palette, colorMode) + : state.colorMapping.assignments; +} +export function Container(props: { + palettes: Map; + data: ColorMappingInputData; + isDarkMode: boolean; +}) { + const dispatch = useDispatch(); + + const getPaletteFn = getPalette(props.palettes, NeutralPalette); + + const palette = useSelector(getPaletteSelector(getPaletteFn)); + const colorMode = useSelector(getColorModeSelector); + const autoAssignmentMode = useSelector(isAutoAssignmentModeSelector); + const assignments = useSelector(getComputedAssignmentsSelector(props.data, palette, colorMode)); + const specialAssignments = useSelector(getSpecialAssignmentsSelector); + + const canAddNewAssignment = !autoAssignmentMode && assignments.length < MAX_ASSIGNABLE_COLORS; + + return ( + + + + + + + Assignments + + + { + if (autoAssignmentMode) { + dispatch(assignStatically(assignments)); + } else { + dispatch(assignAutomatically()); + } + }} + /> + + + + + + + {colorMode.type !== 'gradient' ? null : ( + + + + )} + + {assignments.map((assignment, i) => { + return ( + + ); + })} + + + + + {props.data.type === 'categories' && ( + <> + + {specialAssignments.map((assignment, i) => { + return ( + + ); + })} + + )} + + + + + + { + dispatch( + addNewAssignment({ + rule: + props.data.type === 'categories' + ? { + type: 'matchExactly', + values: [], + } + : { type: 'range', min: 0, max: 0, minInclusive: true, maxInclusive: true }, + color: getUnusedColorForNewAssignment(palette, colorMode, assignments), + touched: false, + }) + ); + }} + disabled={!canAddNewAssignment} + > + Add assignment + + {colorMode.type === 'gradient' && ( + { + dispatch(changeGradientSortOrder(colorMode.sort === 'asc' ? 'desc' : 'asc')); + }} + > + Invert gradient + + )} + + + ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss new file mode 100644 index 0000000000000..42cf677ef19e5 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss @@ -0,0 +1,28 @@ +.colorMappingGradientAddStop:hover { + opacity: 1 !important; +} +.colorMappingColorSwatchEditable::after { + content: ''; + width: 0; + height: 0; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-top: 4px solid white; + margin: 0; + bottom: 2px; + position: absolute; + right: 2.5px; +} + +.colorMappingColorSwatchEditableRound::after { + content: ''; + width: 0; + height: 0; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-top: 4px solid white; + margin: 0; + bottom: 4px; + position: absolute; + right: 5px; +} \ No newline at end of file diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx new file mode 100644 index 0000000000000..caa312772c3b6 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -0,0 +1,261 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { useDispatch } from 'react-redux'; +import { scaleSequential } from 'd3-scale'; +import { interpolateHsl, piecewise } from 'd3-interpolate'; +import { changeAlpha, getValidColor } from '../../color/color_math'; + +import { ColorMapping } from '../../config'; +import { ColorSwatch } from '../color_picker/color_swatch'; +import { getPalette } from '../../palette'; + +import './gradient.scss'; + +import { addGradientColorStep, updateGradientColorStep } from '../../state/color_mapping'; +import { ColorCode, CategoricalColor, CategoricalPalette } from '../../config/types'; +import { colorPickerVisibility } from '../../state/ui'; + +export function Gradient({ + paletteId, + colorMode, + getPaletteFn, + isDarkMode, +}: { + paletteId: string; + isDarkMode: boolean; + colorMode: ColorMapping.Config['colorMode']; + getPaletteFn: ReturnType; +}) { + const currentPalette = getPaletteFn(paletteId); + const gradientColorSteps = + colorMode.type === 'gradient' + ? colorMode.steps.length === 1 + ? [ + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 1), + ].sort(() => (colorMode.sort === 'asc' ? 1 : -1)) + : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)) + : []; + + const gradientColorScale = scaleSequential(piecewise(interpolateHsl, gradientColorSteps)); + const gradientCSSBackground = + colorMode.type === 'gradient' + ? colorMode.steps.length === 1 + ? gradientColorSteps.join(',') + : Array.from({ length: 10 }, (d, i) => gradientColorScale(i / 10)) + .sort(() => (colorMode.sort === 'asc' ? -1 : 1)) + .join(',') + : ''; + + return colorMode.type === 'categorical' ? null : ( +
+
+ + {colorMode.steps.length === 1 ? ( + <> + + + + + + + + ) : colorMode.steps.length === 2 ? ( + <> + + + + + + + + + + + ) : ( + <> + + + + + + + + + + + )} + +
+ ); +} + +function AddStop({ + colorMode, + currentPalette, + at, +}: { + colorMode: { + type: 'gradient'; + steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; + }; + currentPalette: CategoricalPalette; + at: number; +}) { + const dispatch = useDispatch(); + return ( + = 3} + style={{ opacity: 0.3, width: 16, height: 16, paddingLeft: 12 }} + className="colorMappingGradientAddStop" + color="text" + size="s" + onClick={() => { + dispatch( + addGradientColorStep({ + color: { + type: 'categorical', + // TODO assign the next available color or a better one + colorIndex: colorMode.steps.length, + paletteId: currentPalette.id, + }, + at, + }) + ); + dispatch( + colorPickerVisibility({ + index: at, + type: 'gradient', + visible: true, + }) + ); + }} + /> + ); +} + +function ColorStop({ + colorMode, + step, + index, + currentPalette, + getPaletteFn, + isDarkMode, +}: { + colorMode: { + type: 'gradient'; + steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; + sort: 'asc' | 'desc'; + }; + step: CategoricalColor | ColorCode; + index: number; + currentPalette: CategoricalPalette; + getPaletteFn: ReturnType; + isDarkMode: boolean; +}) { + const dispatch = useDispatch(); + return ( + { + dispatch( + updateGradientColorStep({ + index, + color, + }) + ); + }} + forType="gradient" + /> + ); +} + +function getColor( + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode, + getPaletteFn: ReturnType, + isDarkMode: boolean +) { + return color.type === 'colorCode' + ? color.colorCode + : getValidColor(getPaletteFn(color.paletteId).getColor(color.colorIndex, isDarkMode)).hex(); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx new file mode 100644 index 0000000000000..12829dcd4ea1f --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { + EuiButtonGroup, + EuiColorPalettePicker, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, +} from '@elastic/eui'; + +import { RootState, updatePalette } from '../../state/color_mapping'; +import { ColorMapping } from '../../config'; +import { updateAssignmentsPalette, updateColorModePalette } from '../../config/assignments'; +import { getPalette } from '../../palette'; + +export function PaletteSelector({ + palettes, + getPaletteFn, + isDarkMode, +}: { + getPaletteFn: ReturnType; + palettes: Map; + isDarkMode: boolean; +}) { + const dispatch = useDispatch(); + const colorMode = useSelector((state: RootState) => state.colorMapping.colorMode); + const model = useSelector((state: RootState) => state.colorMapping); + + const { paletteId } = model; + + const switchPaletteFn = useCallback( + (selectedPaletteId: string, preserveColorChanges: boolean) => { + dispatch( + updatePalette({ + paletteId: selectedPaletteId, + assignments: updateAssignmentsPalette( + model.assignments, + model.assignmentMode, + model.colorMode, + selectedPaletteId, + getPaletteFn, + preserveColorChanges + ), + colorMode: updateColorModePalette( + model.colorMode, + selectedPaletteId, + preserveColorChanges + ), + }) + ); + }, + [getPaletteFn, model, dispatch] + ); + + const updateColorMode = useCallback( + (type: 'gradient' | 'categorical', preserveColorChanges: boolean) => { + const updatedColorMode: ColorMapping.Config['colorMode'] = + type === 'gradient' + ? { + type: 'gradient', + steps: [ + { + type: 'categorical', + paletteId, + colorIndex: 0, + touched: false, + }, + ], + sort: 'desc', + } + : { type: 'categorical' }; + + const assignments = updateAssignmentsPalette( + model.assignments, + model.assignmentMode, + updatedColorMode, + paletteId, + getPaletteFn, + preserveColorChanges + ); + dispatch(updatePalette({ paletteId, assignments, colorMode: updatedColorMode })); + }, + [getPaletteFn, model, dispatch, paletteId] + ); + + return ( + <> + + + + d.name !== 'Neutral') + .map((palette) => ({ + value: palette.id, + title: palette.name, + palette: Array.from({ length: palette.colorCount }, (_, i) => { + return palette.getColor(i, isDarkMode); + }), + type: 'fixed', + }))} + onChange={(selectedPaletteId) => { + const hasChanges = model.assignments.some((a) => a.touched); + const hasGradientChanges = + model.colorMode.type === 'gradient' && + model.colorMode.steps.some((a) => a.touched); + if (hasChanges || hasGradientChanges) { + const preserve = window.confirm( + 'Do you want to preserve the color changes? (cancel for NO)' + ); + switchPaletteFn(selectedPaletteId, preserve); + } else { + switchPaletteFn(selectedPaletteId, false); + } + }} + valueOfSelected={model.paletteId} + selectionDisplay={'palette'} + /> + + + + + { + const hasChanges = model.assignments.some((a) => a.touched); + const hasGradientChanges = + model.colorMode.type === 'gradient' && + model.colorMode.steps.some((a) => a.touched); + + if (hasChanges || hasGradientChanges) { + const okToSwitch = window.confirm( + `Switch to ${id} will clear all your custom color changes, are you Ok? (cancel for NO)` + ); + if (okToSwitch) { + updateColorMode(id as 'gradient' | 'categorical', false); + } + } else { + updateColorMode(id as 'gradient' | 'categorical', false); + } + }} + isIconOnly + /> + + + + + ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/assignment_from_categories.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/assignment_from_categories.ts new file mode 100644 index 0000000000000..97c4d17c35e4d --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/assignment_from_categories.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from '.'; +import { ColorMappingInputData } from '../categorical_color_mapping'; +import { MAX_ASSIGNABLE_COLORS } from '../components/container/container'; + +export function generateAutoAssignmentsForCategories( + data: ColorMappingInputData, + palette: ColorMapping.CategoricalPalette, + colorMode: ColorMapping.Config['colorMode'] +): ColorMapping.Config['assignments'] { + const isCategorical = colorMode.type === 'categorical'; + + const maxColorAssignable = data.type === 'categories' ? data.categories.length : data.bins; + + const assignableColors = isCategorical + ? Math.min(palette.colorCount, maxColorAssignable) + : Math.min(MAX_ASSIGNABLE_COLORS, maxColorAssignable); + + const autoRules: Array = + data.type === 'categories' + ? data.categories.map((c) => ({ type: 'matchExactly', values: [c] })) + : Array.from({ length: data.bins }, (d, i) => { + const step = (data.max - data.min) / data.bins; + return { + type: 'range', + min: data.max - i * step - step, + max: data.max - i * step, + minInclusive: true, + maxInclusive: false, + }; + }); + + const assignments = autoRules + .slice(0, assignableColors) + .map((rule, colorIndex) => { + if (isCategorical) { + return { + rule, + color: { + type: 'categorical', + paletteId: palette.id, + colorIndex, + }, + touched: false, + }; + } else { + return { + rule, + color: { + type: 'gradient', + }, + touched: false, + }; + } + }); + + return assignments; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts new file mode 100644 index 0000000000000..11d9fcd9add99 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ColorMapping } from '.'; +import { MAX_ASSIGNABLE_COLORS } from '../components/container/container'; +import { DEFAULT_NEUTRAL_PALETTE_INDEX, NeutralPalette } from '../palettes/default_palettes'; +import { getPalette } from '../palette'; + +export function updateAssignmentsPalette( + assignments: ColorMapping.Config['assignments'], + assignmentMode: ColorMapping.Config['assignmentMode'], + colorMode: ColorMapping.Config['colorMode'], + paletteId: string, + getPaletteFn: ReturnType, + preserveColorChanges: boolean +): ColorMapping.Config['assignments'] { + const palette = getPaletteFn(paletteId); + const maxColors = palette.type === 'categorical' ? palette.colorCount : MAX_ASSIGNABLE_COLORS; + return assignmentMode === 'auto' + ? [] + : assignments.map(({ rule, color, touched }, index) => { + if (preserveColorChanges && touched) { + return { rule, color, touched }; + } else { + const newColor: ColorMapping.Config['assignments'][number]['color'] = + colorMode.type === 'categorical' + ? { + type: 'categorical', + paletteId: index < maxColors ? paletteId : NeutralPalette.id, + colorIndex: index < maxColors ? index : 0, + } + : { type: 'gradient' }; + return { + rule, + color: newColor, + touched: false, + }; + } + }); +} + +export function updateColorModePalette( + colorMode: ColorMapping.Config['colorMode'], + paletteId: string, + preserveColorChanges: boolean +): ColorMapping.Config['colorMode'] { + return colorMode.type === 'categorical' + ? colorMode + : { + type: 'gradient', + steps: colorMode.steps.map((step, stepIndex) => { + return preserveColorChanges + ? step + : { type: 'categorical', paletteId, colorIndex: stepIndex, touched: false }; + }), + sort: colorMode.sort, + }; +} + +export function getUnusedColorForNewAssignment( + palette: ColorMapping.CategoricalPalette, + colorMode: ColorMapping.Config['colorMode'], + assignments: ColorMapping.Config['assignments'] +): ColorMapping.Config['assignments'][number]['color'] { + if (colorMode.type === 'categorical') { + // TODO: change the type of color assignment depending on palette + // compute the next unused color index in the palette. + const maxColors = palette.type === 'categorical' ? palette.colorCount : MAX_ASSIGNABLE_COLORS; + const colorIndices = new Set(Array.from({ length: maxColors }, (d, i) => i)); + assignments.forEach(({ color }) => { + if (color.type === 'categorical' && color.paletteId === palette.id) { + colorIndices.delete(color.colorIndex); + } + }); + const paletteForNextUnusedColorIndex = colorIndices.size > 0 ? palette.id : NeutralPalette.id; + const nextUnusedColorIndex = + colorIndices.size > 0 ? [...colorIndices][0] : DEFAULT_NEUTRAL_PALETTE_INDEX; + return { + type: 'categorical', + paletteId: paletteForNextUnusedColorIndex, + colorIndex: nextUnusedColorIndex, + }; + } else { + return { type: 'gradient' }; + } +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts new file mode 100644 index 0000000000000..375d93fcf1789 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from '.'; +import { NeutralPalette, availablePalettes, getPalette } from '../palettes/default_palettes'; + +export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { + assignmentMode: 'auto', + assignments: [], + specialAssignments: [ + { + rule: { + type: 'other', + }, + color: { + type: 'categorical', + paletteId: NeutralPalette.id, + colorIndex: 2, + }, + touched: false, + }, + ], + paletteId: 'eui', + colorMode: { + type: 'categorical', + }, +}; + +export function getPaletteColors( + isDarkMode: boolean, + colorMappings?: ColorMapping.Config +): string[] { + const colorMappingModel = colorMappings ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }; + const palette = getPalette(availablePalettes, NeutralPalette)(colorMappingModel.paletteId); + return Array.from({ length: palette.colorCount }, (d, i) => palette.getColor(i, isDarkMode)); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/index.ts new file mode 100644 index 0000000000000..e75687596789e --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as ColorMapping from './types'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts new file mode 100644 index 0000000000000..959f6b11fb505 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type Color = string; + +export interface ColorCode { + type: 'colorCode'; + colorCode: Color; +} + +export interface CategoricalColor { + type: 'categorical'; + paletteId: string; + colorIndex: number; +} + +export interface GradientColor { + type: 'gradient'; +} + +export interface RuleAuto { + /* tag */ + type: 'auto'; +} +export interface RuleMatchExactly { + /* tag */ + type: 'matchExactly'; + values: Array; +} + +export interface RuleMatchExactlyCI { + /* tag */ + type: 'matchExactlyCI'; + values: string[]; +} + +export interface RuleRange { + /* tag */ + type: 'range'; + /** + * The min value of the range + */ + min: number; + /** + * The max value of the range + */ + max: number; + /** + * `true` if the range is left-closed (the `min` value is considered within the range), false otherwise (only values that are + * greater than the `min` are considered within the range) + */ + minInclusive: boolean; + /** + * `true` if the range is right-closed (the `max` value is considered within the range), false otherwise (only values less than + * the `max` are considered within the range) + */ + maxInclusive: boolean; +} +export interface RuleRegExp { + /* tag */ + type: 'regex'; + /** + * TODO: not sure how we can store a regexp + */ + values: string; +} + +// TODO: add RulerForSomeOtherFormOfObject +export interface RuleOthers { + /* tag */ + type: 'other'; +} + +export interface Assignment { + /** + * Describe the rule used to assign the color. + */ + rule: R; + /** + * The color definition + */ + color: C; + + /** + * Specify if the color was changed from the original one + * TODO: rename + */ + touched: boolean; +} + +export interface Config { + paletteId: string; + colorMode: + | { type: 'categorical' } + | { + type: 'gradient'; + steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; + sort: 'asc' | 'desc'; + }; + assignmentMode: 'auto' | 'manual'; + assignments: Array< + Assignment< + RuleAuto | RuleMatchExactly | RuleMatchExactlyCI | RuleRange | RuleRegExp, + CategoricalColor | ColorCode | GradientColor + > + >; + specialAssignments: Array>; +} + +export interface CategoricalPalette { + id: string; + name: string; + type: 'categorical'; + colorCount: number; + getColor: (valueInRange: number, isDarkMode: boolean) => Color; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts new file mode 100644 index 0000000000000..2c084e2f8e185 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { CategoricalColorMapping } from './categorical_color_mapping'; +export type { ColorMappingInputData } from './categorical_color_mapping'; +export type { ColorMapping } from './config'; +export * from './palettes/default_palettes'; +export * from './color/color_handling'; +export { SPECIAL_RULE_MATCHES } from './color/rule_matching'; +export { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors } from './config/default_color_mapping'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palette.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palette.ts new file mode 100644 index 0000000000000..9ec94c9355977 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palette.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from './config'; + +/** + * This function should be instanciated once at the root of the component with the available palettes and + * a choosed default one and shared across components to keep a single point of truth of the available palettes and the default + * one. + */ +export function getPalette( + palettes: Map, + defaultPalette: ColorMapping.CategoricalPalette +): (paletteId: string) => ColorMapping.CategoricalPalette { + return (paletteId) => palettes.get(paletteId) ?? defaultPalette; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts new file mode 100644 index 0000000000000..74880d3f04b69 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EUI_PALETTE_COLORS_DARK, EUI_PALETTE_COLORS_LIGHT } from './eui'; +import { TABLEAU_COLORS_DARK, TABLEAU_COLORS_LIGHT } from './tableau'; +import { IKEA_COLORS_DARK, IKEA_COLORS_LIGHT } from './ikea'; +import { PASTEL_PALETTE_DARK, PASTEL_PALETTE_LIGHT } from './pastel'; +import { NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from './neutral'; + +import { ColorMapping } from '../config'; + +export const DEFAULT_MISSING_COLOR = 'red'; + +export const EUIPalette: ColorMapping.CategoricalPalette = { + id: 'eui', + name: 'EUI', + colorCount: EUI_PALETTE_COLORS_LIGHT.length, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return isDarkMode + ? EUI_PALETTE_COLORS_DARK[valueInRange] + : EUI_PALETTE_COLORS_LIGHT[valueInRange]; + }, +}; + +export const TableauPalette: ColorMapping.CategoricalPalette = { + id: 'tableau', + name: 'Tableau', + colorCount: TABLEAU_COLORS_LIGHT.length, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return isDarkMode ? TABLEAU_COLORS_DARK[valueInRange] : TABLEAU_COLORS_LIGHT[valueInRange]; + }, +}; + +export const IKEAPalette: ColorMapping.CategoricalPalette = { + id: 'ikea', + name: 'IKEA', + colorCount: IKEA_COLORS_LIGHT.length, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return isDarkMode ? IKEA_COLORS_DARK[valueInRange] : IKEA_COLORS_LIGHT[valueInRange]; + }, +}; + +export const PastelPalette: ColorMapping.CategoricalPalette = { + id: 'pastel', + name: 'Pastel', + colorCount: PASTEL_PALETTE_LIGHT.length, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return isDarkMode ? PASTEL_PALETTE_DARK[valueInRange] : PASTEL_PALETTE_LIGHT[valueInRange]; + }, +}; + +export const NeutralPalette: ColorMapping.CategoricalPalette = { + id: 'neutral', + name: 'Neutral', + colorCount: NEUTRAL_COLOR_LIGHT.length, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return isDarkMode ? NEUTRAL_COLOR_DARK[valueInRange] : NEUTRAL_COLOR_LIGHT[valueInRange]; + }, +}; + +export const DEFAULT_NEUTRAL_PALETTE_INDEX = 2; + +export const availablePalettes = new Map([ + [EUIPalette.id, EUIPalette], + [TableauPalette.id, TableauPalette], + [IKEAPalette.id, IKEAPalette], + [PastelPalette.id, PastelPalette], + [NeutralPalette.id, NeutralPalette], +]); + +export function getPalette( + palettes: Map, + defaultPalette: ColorMapping.CategoricalPalette +) { + return (paletteId: string) => palettes.get(paletteId) ?? defaultPalette; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts new file mode 100644 index 0000000000000..a10bdf3c9ed02 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EUI_PALETTE_COLORS_LIGHT = [ + '#54B399', + '#6092C0', + '#D36086', + '#9170B8', + '#CA8EAE', + '#D6BF57', + '#B9A888', + '#DA8B45', + '#AA6556', + '#E7664C', +]; +export const EUI_PALETTE_COLORS_DARK = [ + '#54B399', + '#6092C0', + '#D36086', + '#9170B8', + '#CA8EAE', + '#D6BF57', + '#B9A888', + '#DA8B45', + '#AA6556', + '#E7664C', +]; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts new file mode 100644 index 0000000000000..2a83dc3e913c7 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const IKEA_COLORS_LIGHT = ['#0057ad', '#af52b2', '#ff5688', '#ff8f48', '#fbda0c']; +export const IKEA_COLORS_DARK = ['#0057ad', '#af52b2', '#ff5688', '#ff8f48', '#fbda0c']; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts new file mode 100644 index 0000000000000..4f627dea75e66 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const schemeGreys = ['#f7f7f7', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525']; +export const NEUTRAL_COLOR_LIGHT = schemeGreys.slice(1, 6); +export const NEUTRAL_COLOR_DARK = schemeGreys.slice(1, 6); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts new file mode 100644 index 0000000000000..d4b5bbaaef85b --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const PASTEL_PALETTE_LIGHT = [ + '#fbb4ae', + '#b3cde3', + '#ccebc5', + '#decbe4', + '#fed9a6', + '#ffffcc', + '#e5d8bd', + '#fddaec', +]; +export const PASTEL_PALETTE_DARK = [ + '#fbb4ae', + '#b3cde3', + '#ccebc5', + '#decbe4', + '#fed9a6', + '#ffffcc', + '#e5d8bd', + '#fddaec', +]; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts new file mode 100644 index 0000000000000..23ffa4d7a6a30 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const TABLEAU_COLORS_LIGHT = [ + '#4e79a7', + '#f28e2c', + '#e15759', + '#76b7b2', + '#59a14f', + '#edc949', + '#af7aa1', + '#ff9da7', + '#9c755f', + '#bab0ab', +]; +export const TABLEAU_COLORS_DARK = [ + '#4e79a7', + '#f28e2c', + '#e15759', + '#76b7b2', + '#59a14f', + '#edc949', + '#af7aa1', + '#ff9da7', + '#9c755f', + '#bab0ab', +]; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts new file mode 100644 index 0000000000000..ad7f2380b8257 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts @@ -0,0 +1,210 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createSlice } from '@reduxjs/toolkit'; +import type { PayloadAction } from '@reduxjs/toolkit'; +import type { ColorMapping } from '../config'; + +export interface RootState { + colorMapping: ColorMapping.Config; + ui: { + colorPicker: { + index: number; + visibile: boolean; + type: 'gradient' | 'assignment' | 'specialAssignment'; + }; + }; +} + +const initialState: RootState['colorMapping'] = { + assignmentMode: 'auto', + assignments: [], + specialAssignments: [], + paletteId: 'eui', + colorMode: { type: 'categorical' }, +}; + +export const colorMappingSlice = createSlice({ + name: 'colorMapping', + initialState, + reducers: { + updateModel: (state, action: PayloadAction) => { + state.assignmentMode = action.payload.assignmentMode; + state.assignments = [...action.payload.assignments]; + state.specialAssignments = [...action.payload.specialAssignments]; + state.paletteId = action.payload.paletteId; + state.colorMode = { ...action.payload.colorMode }; + }, + updatePalette: ( + state, + action: PayloadAction<{ + assignments: ColorMapping.Config['assignments']; + paletteId: ColorMapping.Config['paletteId']; + colorMode: ColorMapping.Config['colorMode']; + }> + ) => { + state.paletteId = action.payload.paletteId; + state.assignments = [...action.payload.assignments]; + state.colorMode = { ...action.payload.colorMode }; + }, + assignStatically: (state, action: PayloadAction) => { + state.assignmentMode = 'manual'; + state.assignments = [...action.payload]; + }, + assignAutomatically: (state) => { + state.assignmentMode = 'auto'; + state.assignments = []; + }, + + addNewAssignment: ( + state, + action: PayloadAction + ) => { + state.assignments.push({ ...action.payload }); + }, + updateAssignment: ( + state, + action: PayloadAction<{ + assignmentIndex: number; + assignment: ColorMapping.Config['assignments'][number]; + }> + ) => { + state.assignments[action.payload.assignmentIndex] = { + ...action.payload.assignment, + touched: true, + }; + }, + updateAssignmentRule: ( + state, + action: PayloadAction<{ + assignmentIndex: number; + rule: ColorMapping.Config['assignments'][number]['rule']; + }> + ) => { + state.assignments[action.payload.assignmentIndex] = { + ...state.assignments[action.payload.assignmentIndex], + rule: action.payload.rule, + }; + }, + updateAssignmentColor: ( + state, + action: PayloadAction<{ + assignmentIndex: number; + color: ColorMapping.Config['assignments'][number]['color']; + }> + ) => { + state.assignments[action.payload.assignmentIndex] = { + ...state.assignments[action.payload.assignmentIndex], + color: action.payload.color, + touched: true, + }; + }, + + updateSpecialAssignmentColor: ( + state, + action: PayloadAction<{ + assignmentIndex: number; + color: ColorMapping.Config['specialAssignments'][number]['color']; + }> + ) => { + state.specialAssignments[action.payload.assignmentIndex] = { + ...state.specialAssignments[action.payload.assignmentIndex], + color: action.payload.color, + touched: true, + }; + }, + removeAssignment: (state, action: PayloadAction) => { + state.assignments.splice(action.payload, 1); + }, + changeColorMode: (state, action: PayloadAction) => { + state.colorMode = { ...action.payload }; + }, + updateGradientColorStep: ( + state, + action: PayloadAction<{ + index: number; + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode; + }> + ) => { + if (state.colorMode.type !== 'gradient') { + return; + } + + state.colorMode = { + ...state.colorMode, + steps: state.colorMode.steps.map((step, index) => { + return index === action.payload.index + ? { ...action.payload.color, touched: true } + : { ...step, touched: false }; + }), + }; + }, + removeGradientColorStep: (state, action: PayloadAction) => { + if (state.colorMode.type !== 'gradient') { + return; + } + const steps = [...state.colorMode.steps]; + steps.splice(action.payload, 1); + state.colorMode = { + ...state.colorMode, + steps: [...steps], + }; + }, + addGradientColorStep: ( + state, + action: PayloadAction<{ + color: ColorMapping.CategoricalColor | ColorMapping.ColorCode; + at: number; + }> + ) => { + if (state.colorMode.type !== 'gradient') { + return; + } + + state.colorMode = { + ...state.colorMode, + steps: [ + ...state.colorMode.steps.slice(0, action.payload.at), + { ...action.payload.color, touched: false }, + ...state.colorMode.steps.slice(action.payload.at), + ], + }; + }, + + changeGradientSortOrder: (state, action: PayloadAction<'asc' | 'desc'>) => { + if (state.colorMode.type !== 'gradient') { + return; + } + + state.colorMode = { + ...state.colorMode, + sort: action.payload, + }; + }, + }, +}); +// Action creators are generated for each case reducer function +export const { + updatePalette, + assignStatically, + assignAutomatically, + addNewAssignment, + updateAssignment, + updateAssignmentColor, + updateSpecialAssignmentColor, + updateAssignmentRule, + removeAssignment, + changeColorMode, + updateGradientColorStep, + removeGradientColorStep, + addGradientColorStep, + changeGradientSortOrder, + updateModel, +} = colorMappingSlice.actions; + +export const colorMappingReducer = colorMappingSlice.reducer; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts b/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts new file mode 100644 index 0000000000000..24d5c60a8740d --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getPalette } from '../palette'; +import { RootState } from './color_mapping'; + +export function getPaletteSelector(getPaletteFn: ReturnType) { + return (state: RootState) => getPaletteFn(state.colorMapping.paletteId); +} +export function getColorModeSelector(state: RootState) { + return state.colorMapping.colorMode; +} +export function getSpecialAssignmentsSelector(state: RootState) { + return state.colorMapping.specialAssignments; +} +export function isAutoAssignmentModeSelector(state: RootState) { + return state.colorMapping.assignmentMode === 'auto'; +} +export function getColorPickerVisibilitySelector(state: RootState) { + return state.ui.colorPicker; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/state/ui.ts b/packages/kbn-coloring/src/shared_components/color_mapping/state/ui.ts new file mode 100644 index 0000000000000..632fb31e9dcc5 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/state/ui.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { type PayloadAction, createSlice } from '@reduxjs/toolkit'; +import { RootState } from './color_mapping'; + +const initialState: RootState['ui'] = { + colorPicker: { + index: 0, + visibile: false, + type: 'assignment', + }, +}; + +export const uiSlice = createSlice({ + name: 'colorMapping', + initialState, + reducers: { + colorPickerVisibility: ( + state, + action: PayloadAction<{ + index: number; + type: RootState['ui']['colorPicker']['type']; + visible: boolean; + }> + ) => { + state.colorPicker.visibile = action.payload.visible; + state.colorPicker.index = action.payload.index; + state.colorPicker.type = action.payload.type; + }, + switchColorPickerVisibility: (state) => { + state.colorPicker.visibile = !state.colorPicker.visibile; + }, + showColorPickerVisibility: (state) => { + state.colorPicker.visibile = true; + }, + hideColorPickerVisibility: (state) => { + state.colorPicker.visibile = false; + }, + }, +}); + +export const { + colorPickerVisibility, + switchColorPickerVisibility, + showColorPickerVisibility, + hideColorPickerVisibility, +} = uiSlice.actions; + +export const uiReducer = uiSlice.reducer; diff --git a/packages/kbn-coloring/src/shared_components/index.ts b/packages/kbn-coloring/src/shared_components/index.ts index 546224092e576..242df23b19e53 100644 --- a/packages/kbn-coloring/src/shared_components/index.ts +++ b/packages/kbn-coloring/src/shared_components/index.ts @@ -21,3 +21,5 @@ export const CustomizablePaletteLazy = React.lazy(() => import('./coloring')); * a predefined fallback and error boundary. */ export const CustomizablePalette = withSuspense(CustomizablePaletteLazy); + +export * from './color_mapping'; From ff5fdb4db2164a1fae3a6eff06db22554d9c3aaf Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 25 Aug 2023 17:04:42 +0200 Subject: [PATCH 02/71] feat: add multiFieldKey typeguard --- src/plugins/data/common/search/aggs/buckets/index.ts | 2 +- .../data/common/search/aggs/buckets/multi_field_key.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/search/aggs/buckets/index.ts b/src/plugins/data/common/search/aggs/buckets/index.ts index 31bc7cf9ca544..1d765190d1bdb 100644 --- a/src/plugins/data/common/search/aggs/buckets/index.ts +++ b/src/plugins/data/common/search/aggs/buckets/index.ts @@ -37,7 +37,7 @@ export * from './significant_text_fn'; export * from './significant_text'; export * from './terms_fn'; export * from './terms'; -export { MultiFieldKey } from './multi_field_key'; +export { MultiFieldKey, isMultiFieldKey } from './multi_field_key'; export * from './multi_terms_fn'; export * from './multi_terms'; export * from './rare_terms_fn'; diff --git a/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts b/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts index 89ac1f4c00a54..dba06e7d3683f 100644 --- a/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts +++ b/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts @@ -38,3 +38,7 @@ export class MultiFieldKey { return this[id]; } } + +export function isMultiFieldKey(field: unknown): field is MultiFieldKey { + return field instanceof MultiFieldKey; +} From 324a54e8cce792fbaee88ec484db6655cdeb7270 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 25 Aug 2023 17:16:46 +0200 Subject: [PATCH 03/71] feat: intregrate color mapping in XY --- .../common_data_layer_args.ts | 4 + .../common/expression_functions/xy_vis_fn.ts | 1 + .../expression_xy/common/i18n/index.tsx | 4 + .../common/types/expression_functions.ts | 2 + .../public/components/data_layers.tsx | 3 + .../public/components/xy_chart.tsx | 1 + .../public/helpers/color/categories.ts | 30 ++++ .../public/helpers/color/color_mapping.ts | 40 ++++++ .../public/helpers/color_assignment.ts | 5 + .../public/helpers/data_layers.tsx | 60 ++++++-- .../expression_xy/public/index.ts | 2 + .../public/visualizations/xy/to_expression.ts | 3 +- .../lens/public/visualizations/xy/types.ts | 3 +- .../visualizations/xy/visualization.tsx | 22 ++- .../xy/xy_config_panel/dimension_editor.tsx | 132 +++++++++++++++--- 15 files changed, 273 insertions(+), 39 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts index 10f6d5d748b23..b9e2bd6dbac67 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/common_data_layer_args.ts @@ -94,4 +94,8 @@ export const commonDataLayerArgs: Omit< help: strings.getPaletteHelp(), default: '{palette}', }, + colorMapping: { + types: ['string'], + help: strings.getColorMappingHelp(), + }, }; diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts index 94d788106acb3..03df575b3c653 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/xy_vis_fn.ts @@ -52,6 +52,7 @@ const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult layerType: LayerTypes.DATA, table: normalizedTable, showLines: args.showLines, + colorMapping: args.colorMapping, ...accessors, }; }; diff --git a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx index d9fc015c2844c..2446a27e718ce 100644 --- a/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx +++ b/src/plugins/chart_expressions/expression_xy/common/i18n/index.tsx @@ -209,6 +209,10 @@ export const strings = { i18n.translate('expressionXY.dataLayer.palette.help', { defaultMessage: 'Palette', }), + getColorMappingHelp: () => + i18n.translate('expressionXY.layer.colorMapping.help', { + defaultMessage: 'JSON key-value pairs of the color mapping model', + }), getTableHelp: () => i18n.translate('expressionXY.layers.table.help', { defaultMessage: 'Table', diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 55fd63786570b..19e6bad146d62 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -136,6 +136,7 @@ export interface DataLayerArgs { isStacked: boolean; isHorizontal: boolean; palette: PaletteOutput; + colorMapping: string; // JSON stringified object of the color mapping decorations?: DataDecorationConfigResult[]; curveType?: XYCurveType; } @@ -163,6 +164,7 @@ export interface ExtendedDataLayerArgs { isStacked: boolean; isHorizontal: boolean; palette: PaletteOutput; + colorMapping: string; // palette will always be set on the expression decorations?: DataDecorationConfigResult[]; curveType?: XYCurveType; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx index 5cabeaee31575..cc6e969a10af9 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/data_layers.tsx @@ -57,6 +57,7 @@ interface Props { fieldFormats: LayersFieldFormats; uiState?: PersistedState; singleTable?: boolean; + isDarkMode: boolean; } export const DataLayers: FC = ({ @@ -80,6 +81,7 @@ export const DataLayers: FC = ({ fieldFormats, uiState, singleTable, + isDarkMode, }) => { // for singleTable mode we should use y accessors from all layers for creating correct series name and getting color const allYAccessors = layers.flatMap((layer) => layer.accessors); @@ -169,6 +171,7 @@ export const DataLayers: FC = ({ allYAccessors, singleTable, multipleLayersWithSplits, + isDarkMode, }); const index = `${layer.layerId}-${accessorIndex}`; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index b8ac9d5cd0bbb..c241e476db5de 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -964,6 +964,7 @@ export function XYChart({ fieldFormats={fieldFormats} uiState={uiState} singleTable={singleTable} + isDarkMode={darkMode} /> )} {referenceLineLayers.length ? ( diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts new file mode 100644 index 0000000000000..3f81c68b40710 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { DatatableRow } from '@kbn/expressions-plugin/common'; + +export function getColorCategories(rows: DatatableRow[], accessor?: string) { + return accessor + ? rows.reduce<{ keys: Set; array: Array }>( + (acc, r) => { + const value = r[accessor]; + if (value === undefined) { + return acc; + } + const key = value.hasOwnProperty('keys') ? [...value.keys] : [value]; + const stringifiedKeys = key.join(','); + if (!acc.keys.has(stringifiedKeys)) { + acc.keys.add(stringifiedKeys); + acc.array.push(key.length === 1 ? key[0] : key); + } + return acc; + }, + { keys: new Set(), array: [] } + ).array + : []; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts new file mode 100644 index 0000000000000..0c589bbeead51 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SeriesColorAccessorFn } from '@elastic/charts'; +import { getColorFactory, type ColorMapping, type ColorMappingInputData } from '@kbn/coloring'; +export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; +export function getColorSeriesAccessorFn( + model: ColorMapping.Config, + getPaletteFn: (paletteId: string) => ColorMapping.CategoricalPalette, + isDarkMode: boolean, + mappingData: ColorMappingInputData, + fieldId: string +): SeriesColorAccessorFn { + const specialHandlingInverse: Map = + mappingData.type === 'categories' + ? new Map([...mappingData.specialHandling.entries()].map((d) => [d[1], d[0]])) + : new Map(); + const getColor = getColorFactory(model, getPaletteFn, isDarkMode, mappingData); + return ({ splitAccessors }) => { + const category = splitAccessors.get(fieldId); + if (category === undefined) { + return 'red'; + } + const stringCategory = `${category}`; + // if the separator exist, we are de-constructing a multifieldkey into values. + const categoryForColor = stringCategory.includes(MULTI_FIELD_VALUES_SEPARATOR) + ? stringCategory.split(MULTI_FIELD_VALUES_SEPARATOR).map((c) => { + const trimmedValue = c.trim(); + return specialHandlingInverse.get(trimmedValue) ?? trimmedValue; + }) + : specialHandlingInverse.get(stringCategory) ?? stringCategory; + + return getColor(categoryForColor); + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts index 94b187055e6dd..990d1ab93a1bc 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color_assignment.ts @@ -95,6 +95,11 @@ export const getAllSeries = ( return allSeries; }; +/** + * This function joins every data series name available on each layer by the same color palette. + * The returned function `getRank` should return the position of a series name in this unified list by palette. + * + */ export function getColorAssignments( layers: CommonXYLayerConfig[], titles: LayersAccessorsTitles, diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index ff76ec511ffc9..3eb08c3b790b1 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -16,13 +16,16 @@ import { SeriesName, StackMode, XYChartSeriesIdentifier, + SeriesColorAccessorFn, } from '@elastic/charts'; import { IFieldFormat } from '@kbn/field-formats-plugin/common'; import type { PersistedState } from '@kbn/visualizations-plugin/public'; import { Datatable } from '@kbn/expressions-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; -import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; +import { ColorMapping, PaletteRegistry, SeriesLayer } from '@kbn/coloring'; +import { getPalette, availablePalettes, NeutralPalette } from '@kbn/coloring'; +import { SPECIAL_RULE_MATCHES } from '@kbn/coloring/src/shared_components/color_mapping/color/rule_matching'; import { isDataLayer } from '../../common/utils/layer_types_guards'; import { CommonXYDataLayerConfig, CommonXYLayerConfig, XScaleType } from '../../common'; import { AxisModes, SeriesTypes } from '../../common/constants'; @@ -32,6 +35,8 @@ import { ColorAssignments } from './color_assignment'; import { GroupsConfiguration } from './axes_configuration'; import { LayerAccessorsTitles, LayerFieldFormats, LayersFieldFormats } from './layers'; import { getFormat } from './format'; +import { getColorSeriesAccessorFn } from './color/color_mapping'; +import { getColorCategories } from './color/categories'; type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; @@ -57,6 +62,7 @@ type GetSeriesPropsFn = (config: { allYAccessors: Array; singleTable?: boolean; multipleLayersWithSplits: boolean; + isDarkMode: boolean; }) => SeriesSpec; type GetSeriesNameFn = ( @@ -399,6 +405,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ allYAccessors, singleTable, multipleLayersWithSplits, + isDarkMode, }): SeriesSpec => { const { table, isStacked, markSizeAccessor } = layer; const isPercentage = layer.isPercentage; @@ -478,6 +485,43 @@ export const getSeriesProps: GetSeriesPropsFn = ({ ); }; + const splitCategories = getColorCategories( + table.rows, + splitColumnIds.length > 0 ? splitColumnIds[0] : undefined + ); + + const colorMappingModel: ColorMapping.Config = JSON.parse(layer.colorMapping); + + // TODO: to be replaced by a check for new chart vs existing charts + const canUseColorMapping = true; + const colorFn: SeriesColorAccessorFn = + // for the MVP just apply color mapping if we have a breakdown by configured + canUseColorMapping && splitColumnIds.length > 0 + ? getColorSeriesAccessorFn( + colorMappingModel, + getPalette(availablePalettes, NeutralPalette), + isDarkMode, + { + type: 'categories', + categories: splitCategories, + specialHandling: SPECIAL_RULE_MATCHES, + }, + splitColumnIds[0] + ) + : (series) => + getColor( + series, + { + layer, + colorAssignments, + paletteService, + getSeriesNameFn, + syncColors, + }, + uiState, + singleTable + ); + return { splitSeriesAccessors: splitColumnIds.length ? splitColumnIds : [], stackAccessors: isStacked ? [xColumnId || 'unifiedX'] : [], @@ -497,19 +541,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ formatter?.id === 'bytes' && scaleType === ScaleType.Linear ? ScaleType.LinearBinary : scaleType, - color: (series) => - getColor( - series, - { - layer, - colorAssignments, - paletteService, - getSeriesNameFn, - syncColors, - }, - uiState, - singleTable - ), + color: colorFn, groupId: yAxis?.groupId, enableHistogramMode, stackMode, diff --git a/src/plugins/chart_expressions/expression_xy/public/index.ts b/src/plugins/chart_expressions/expression_xy/public/index.ts index 7c4e39927d127..f2b03a9274c07 100755 --- a/src/plugins/chart_expressions/expression_xy/public/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/index.ts @@ -17,3 +17,5 @@ export function plugin() { export { LayerTypes, XYCurveTypes } from '../common'; export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; + +export { getColorCategories } from './helpers/color/categories'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 4e8264a733398..9f5bc0e9e1f53 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -7,7 +7,7 @@ import { Ast } from '@kbn/interpreter'; import { Position, ScaleType } from '@elastic/charts'; -import type { PaletteRegistry } from '@kbn/coloring'; +import { DEFAULT_COLOR_MAPPING_CONFIG, PaletteRegistry } from '@kbn/coloring'; import { buildExpression, buildExpressionFunction, @@ -511,6 +511,7 @@ const dataLayerToExpression = ( name: 'default', }), ]).toAst(), + colorMapping: JSON.stringify(layer.colorMapping ?? DEFAULT_COLOR_MAPPING_CONFIG), }); return { diff --git a/x-pack/plugins/lens/public/visualizations/xy/types.ts b/x-pack/plugins/lens/public/visualizations/xy/types.ts index e863f04bcdc05..8961c38b1582a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/types.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/types.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { $Values } from '@kbn/utility-types'; -import type { PaletteOutput } from '@kbn/coloring'; +import type { ColorMapping, PaletteOutput } from '@kbn/coloring'; import type { LegendConfig, AxisExtentConfig, @@ -103,6 +103,7 @@ export interface XYDataLayerConfig { xScaleType?: XScaleType; isHistogram?: boolean; columnToLabel?: string; + colorMapping?: ColorMapping.Config; } export interface XYReferenceLineLayerConfig { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 9f5f9755d1781..964bafd4a2083 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Position } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { PaletteRegistry } from '@kbn/coloring'; +import { PaletteRegistry } from '@kbn/coloring'; import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { CoreStart, SavedObjectReference, ThemeServiceStart } from '@kbn/core/public'; @@ -25,6 +25,8 @@ import type { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; import { isEqual } from 'lodash'; import { type AccessorConfig, DimensionTrigger } from '@kbn/visualization-ui-components'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { getPaletteColors } from '@kbn/coloring/src/shared_components/color_mapping/config/default_color_mapping'; +import useObservable from 'react-use/lib/useObservable'; import { generateId } from '../../id_generator'; import { isDraggedDataViewField, @@ -416,6 +418,15 @@ export const getXyVisualization = ({ } ).length < 2; + let colors: string[] = []; + kibanaTheme.theme$ + .subscribe({ + next(theme) { + colors = getPaletteColors(theme.darkMode, layer.colorMapping); + }, + }) + .unsubscribe(); + return { groups: [ { @@ -447,11 +458,7 @@ export const getXyVisualization = ({ { columnId: dataLayer.splitAccessor, triggerIconType: dataLayer.collapseFn ? 'aggregate' : 'colorBy', - palette: dataLayer.collapseFn - ? undefined - : paletteService - .get(dataLayer.palette?.name || 'default') - .getCategoricalColors(10, dataLayer.palette?.params), + palette: dataLayer.collapseFn ? undefined : colors, }, ] : [], @@ -641,13 +648,14 @@ export const getXyVisualization = ({ formatFactory: fieldFormats.deserialize, paletteService, }; + const darkMode: boolean = useObservable(kibanaTheme.theme$, { darkMode: false }).darkMode; const layer = props.state.layers.find((l) => l.layerId === props.layerId)!; const dimensionEditor = isReferenceLayer(layer) ? ( ) : isAnnotationsLayer(layer) ? ( ) : ( - + ); return dimensionEditor; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 94c7a326ccbb4..7c1d3bb6981ea 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -5,17 +5,40 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui'; -import type { PaletteRegistry } from '@kbn/coloring'; import { useDebouncedValue } from '@kbn/visualization-ui-components'; import { ColorPicker } from '@kbn/visualization-ui-components'; + +import { + EuiButtonEmpty, + EuiButtonGroup, + EuiColorPaletteDisplay, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + htmlIdGenerator, +} from '@elastic/eui'; +import { + PaletteRegistry, + ColorMapping, + EUIPalette, + IKEAPalette, + NeutralPalette, + PastelPalette, + TableauPalette, + DEFAULT_COLOR_MAPPING_CONFIG, + getPaletteColors, + CategoricalColorMapping, + SPECIAL_RULE_MATCHES, +} from '@kbn/coloring'; + +import { getColorCategories } from '@kbn/expression-xy-plugin/public'; import type { VisualizationDimensionEditorProps } from '../../../types'; import { State, XYState, XYDataLayerConfig, YConfig, YAxisMode } from '../types'; import { FormatFactory } from '../../../../common/types'; import { getSeriesColor, isHorizontalChart } from '../state_helpers'; -import { PalettePicker } from '../../../shared_components'; +import { PalettePanelContainer, PalettePicker } from '../../../shared_components'; import { getDataLayers } from '../visualization_helpers'; import { CollapseSetting } from '../../../shared_components/collapse_setting'; import { getSortedAccessors } from '../to_expression'; @@ -43,12 +66,15 @@ export function DataDimensionEditor( props: VisualizationDimensionEditorProps & { formatFactory: FormatFactory; paletteService: PaletteRegistry; + darkMode: boolean; } ) { - const { state, setState, layerId, accessor } = props; + const { state, layerId, accessor, darkMode } = props; const index = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[index] as XYDataLayerConfig; + const [isPaletteOpen, setIsPaletteOpen] = useState(false); + const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ value: props.state, onChange: props.setState, @@ -79,6 +105,13 @@ export function DataDimensionEditor( [accessor, index, localState, layer, setLocalState] ); + const setColorMapping = useCallback( + (colorMapping: ColorMapping.Config) => { + setLocalState(updateLayer(localState, { ...layer, colorMapping }, index)); + }, + [index, localState, layer, setLocalState] + ); + const overwriteColor = getSeriesColor(layer, accessor); const assignedColor = useMemo(() => { const sortedAccessors: string[] = getSortedAccessors( @@ -104,20 +137,87 @@ export function DataDimensionEditor( ).color; }, [props.frame, props.paletteService, state.layers, accessor, props.formatFactory, layer]); + // TODO: move the available palette elsewhere + const availablePalettes = new Map([ + [EUIPalette.id, EUIPalette], + [TableauPalette.id, TableauPalette], + [IKEAPalette.id, IKEAPalette], + [PastelPalette.id, PastelPalette], + [NeutralPalette.id, NeutralPalette], + ]); + const localLayer: XYDataLayerConfig = layer; - if (props.groupId === 'breakdown') { - return ( - <> - {!layer.collapseFn && ( - { - setState(updateLayer(localState, { ...localLayer, palette: newPalette }, index)); + + const colors = getPaletteColors(props.darkMode, layer.colorMapping); + const table = props.frame.activeData?.[layer.layerId]; + const { splitAccessor } = layer; + const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); + + const canUseColorMapping = true; + if (props.groupId === 'breakdown' && !layer.collapseFn) { + return canUseColorMapping ? ( + + + { + setIsPaletteOpen(!isPaletteOpen); }} /> - )} - + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + size="xs" + flush="both" + > + {i18n.translate('xpack.lens.paletteXYGradient.customize', { + defaultMessage: 'Edit', + })} + + setIsPaletteOpen(!isPaletteOpen)} + > +
+ setColorMapping(model)} + palettes={availablePalettes} + data={{ + type: 'categories', + categories: splitCategories, + specialHandling: SPECIAL_RULE_MATCHES, + }} + /> +
+
+
+
+ ) : ( + { + props.setState(updateLayer(localState, { ...localLayer, palette: newPalette }, index)); + }} + /> ); } From b9a83fdd4afb572c0d114b31248ddc250424f7d9 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 25 Aug 2023 17:19:26 +0200 Subject: [PATCH 04/71] feat: integrate color mapping in partition --- .../common/expression_functions/i18n.ts | 4 + .../mosaic_vis_function.ts | 4 + .../expression_functions/pie_vis_function.ts | 4 + .../treemap_vis_function.ts | 4 + .../waffle_vis_function.ts | 4 + .../common/types/expression_renderers.ts | 1 + .../utils/layers/get_color_from_mappings.ts | 74 +++++++++++ .../public/utils/layers/get_layers.ts | 93 +++++++++++--- x-pack/plugins/lens/common/types.ts | 3 +- .../partition/dimension_editor.tsx | 118 ++++++++++++++++-- .../visualizations/partition/to_expression.ts | 4 +- .../partition/visualization.tsx | 4 +- 12 files changed, 284 insertions(+), 33 deletions(-) create mode 100644 src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts index b312de7bf1583..c74669439b2c3 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts @@ -122,6 +122,10 @@ export const strings = { i18n.translate('expressionPartitionVis.reusable.function.dimension.splitrow', { defaultMessage: 'Row split', }), + getColorMappingHelp: () => + i18n.translate('expressionPartitionVis.layer.colorMapping.help', { + defaultMessage: 'JSON key-value pairs of the color mapping model', + }), }; export const errors = { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index fc863cf73c68c..89aec849adf0a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -110,6 +110,10 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ help: strings.getAriaLabelHelp(), required: false, }, + colorMapping: { + types: ['string'], + help: strings.getColorMappingHelp(), + }, }, fn(context, args, handlers) { const maxSupportedBuckets = 2; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 0cf6522456c62..cc64beabf7090 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -141,6 +141,10 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ help: strings.getAriaLabelHelp(), required: false, }, + colorMapping: { + types: ['string'], + help: strings.getColorMappingHelp(), + }, }, fn(context, args, handlers) { if (args.splitColumn && args.splitRow) { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 2a5d0a6af7a8a..c7bc7557b98bd 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -115,6 +115,10 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => help: strings.getAriaLabelHelp(), required: false, }, + colorMapping: { + types: ['string'], + help: strings.getColorMappingHelp(), + }, }, fn(context, args, handlers) { const maxSupportedBuckets = 2; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index e4176cf6015c1..f50f410847e20 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -114,6 +114,10 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ help: strings.getAriaLabelHelp(), required: false, }, + colorMapping: { + types: ['string'], + help: strings.getColorMappingHelp(), + }, }, fn(context, args, handlers) { if (args.splitColumn && args.splitRow) { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 239253f54491d..18c8d8a7969b7 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -61,6 +61,7 @@ interface VisCommonParams { maxLegendLines: number; legendSize?: LegendSize; ariaLabel?: string; + colorMapping: string; // JSON stringified object of the color mapping } interface VisCommonConfig extends VisCommonParams { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts new file mode 100644 index 0000000000000..ea0e7cf569631 --- /dev/null +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { NodeColorAccessor, PATH_KEY } from '@elastic/charts'; +import { lightenColor } from '@kbn/charts-plugin/public'; +import { MultiFieldKey } from '@kbn/data-plugin/common'; +import { getColorFactory } from '@kbn/coloring'; +import { isMultiFieldKey } from '@kbn/data-plugin/common'; +import { ChartTypes } from '../../../common/types'; + +function getCategoryKeys(category: string | MultiFieldKey): string | string[] { + return isMultiFieldKey(category) ? category.keys : category; +} + +/** + * Pie, donut, treemap and waffle shares the same color mechanism. + */ +const getPieFillColor = + ( + layerIndex: number, + numOfLayers: number, + getColorFn: ReturnType + ): NodeColorAccessor => + (key, sortIndex, node) => { + const path = node[PATH_KEY]; + // the category used to color the pie/donut is at the third level of the path + // first two are: small multiple and pie whole center. + const category = getCategoryKeys(path[2].value); + const color = getColorFn(category); + // increase the lightness of the color on each layer. + return lightenColor(color, layerIndex + 1, numOfLayers); + }; + +/** + * Mosaic has a slight variation in the way color are applied + */ +const getMosaicFillColor = + ( + layerIndex: number, + numOfLayers: number, + getColorFn: ReturnType + ): NodeColorAccessor => + (key, sortIndex, node) => { + // Special case for 2 layer mosaic where the color is per rows and the columns are not colored + if (numOfLayers === 2 && layerIndex === 0) { + // TODO: the chart or kibana background should be used instead. + return 'rgba(0,0,0,0)'; + } + const path = node[PATH_KEY]; + + // the category used to color the pie/donut is at the third level of the `path` when using a single layer mosaic + // and are at fourth level of `path` when using 2 layer mosaic + // first two are: small multiple and pie whole center. + const category = getCategoryKeys(numOfLayers === 2 ? path[3].value : path[2].value); + const color = getColorFn(category); + // increase the lightness of the color on each layer. + return lightenColor(color, layerIndex, numOfLayers); + }; + +export const getPartitionFillColor = ( + chartType: ChartTypes, + layerIndex: number, + numOfLayers: number, + getColorFn: ReturnType +): NodeColorAccessor => { + return chartType === ChartTypes.MOSAIC + ? getMosaicFillColor(layerIndex, numOfLayers, getColorFn) + : getPieFillColor(layerIndex, numOfLayers, getColorFn); +}; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index e4f3e1687e4ad..dc7917296ea05 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -7,16 +7,49 @@ */ import { Datum, PartitionLayer } from '@elastic/charts'; -import type { PaletteRegistry } from '@kbn/coloring'; +import { + ColorMapping, + PaletteRegistry, + getColorFactory, + SPECIAL_RULE_MATCHES, + getPalette, + availablePalettes, + NeutralPalette, +} from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { Datatable, DatatableRow } from '@kbn/expressions-plugin/public'; + import { getDistinctSeries } from '..'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; import { sortPredicateByType, sortPredicateSaveSourceOrder } from './sort_predicate'; import { byDataColorPaletteMap, getColor } from './get_color'; import { getNodeLabel } from './get_node_labels'; +import { getPartitionFillColor } from './get_color_from_mappings'; + +// TODO: export to a reusable function +export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; +export function getColorCategories(rows: DatatableRow[], accessor?: string) { + return accessor + ? rows.reduce<{ keys: Set; array: Array }>( + (acc, r) => { + const value = r[accessor]; + if (value === undefined) { + return acc; + } + const key = value.hasOwnProperty('keys') ? [...value.keys] : [value]; + const stringifiedKeys = key.join(','); + if (!acc.keys.has(stringifiedKeys)) { + acc.keys.add(stringifiedKeys); + acc.array.push(key.length === 1 ? key[0] : key); + } + return acc; + }, + { keys: new Set(), array: [] } + ).array + : []; +} // This is particularly useful in case of a text based languages where // it's no possible to use a missingBucketLabel @@ -62,6 +95,24 @@ export const getLayers = ( const distinctSeries = getDistinctSeries(rows, columns); + // the mosaic configures the main categories in the second column, instead of the first + // as it happens in all the other partition types + const splitCategories = + chartType === ChartTypes.MOSAIC && columns.length === 2 + ? getColorCategories(rows, columns[1]?.id as string) + : getColorCategories(rows, columns[0]?.id as string); + + const colorMappingModel: ColorMapping.Config = JSON.parse(visParams.colorMapping); + + const getColorFromMappings = getColorFactory( + colorMappingModel, + getPalette(availablePalettes, NeutralPalette), + isDarkMode, + { type: 'categories', categories: splitCategories, specialHandling: SPECIAL_RULE_MATCHES } + ); + + const canUseColorMappings = true; + return columns.map((col, layerIndex) => { return { groupByRollup: (d: Datum) => (col.id ? d[col.id] ?? emptySliceLabel : col.name), @@ -75,25 +126,27 @@ export const getLayers = ( ? sortPredicateSaveSourceOrder() : sortPredicateForType, shape: { - fillColor: (key, sortIndex, node) => - getColor( - chartType, - key, - node, - layerIndex, - isSplitChart, - overwriteColors, - distinctSeries, - { columnsLength: columns.length, rowsLength: rows.length }, - visParams, - palettes, - byDataPalette, - syncColors, - isDarkMode, - formatter, - col, - formatters - ), + fillColor: canUseColorMappings + ? getPartitionFillColor(chartType, layerIndex, columns.length, getColorFromMappings) + : (key, sortIndex, node) => + getColor( + chartType, + key, + node, + layerIndex, + isSplitChart, + overwriteColors, + distinctSeries, + { columnsLength: columns.length, rowsLength: rows.length }, + visParams, + palettes, + byDataPalette, + syncColors, + isDarkMode, + formatter, + col, + formatters + ), }, }; }); diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index ff269070a18c2..34baa25120e09 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -8,7 +8,7 @@ import type { Filter, FilterMeta } from '@kbn/es-query'; import type { Position } from '@elastic/charts'; import type { $Values } from '@kbn/utility-types'; -import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; +import { CustomPaletteParams, PaletteOutput, ColorMapping } from '@kbn/coloring'; import type { ColorMode } from '@kbn/charts-plugin/common'; import type { LegendSize } from '@kbn/visualizations-plugin/common'; import { CategoryDisplay, LegendDisplay, NumberDisplay, PieChartTypes } from './constants'; @@ -71,6 +71,7 @@ export interface SharedPieLayerState { legendMaxLines?: number; legendSize?: LegendSize; truncateLegend?: boolean; + colorMapping?: ColorMapping.Config; } export type PieLayerState = SharedPieLayerState & { diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index bea5c7668e9fe..5d059d408a1f7 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -8,11 +8,26 @@ import './toolbar.scss'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import type { PaletteRegistry } from '@kbn/coloring'; +import { + CategoricalColorMapping, + DEFAULT_COLOR_MAPPING_CONFIG, + PaletteRegistry, + getPaletteColors, + ColorMapping, + EUIPalette, + IKEAPalette, + NeutralPalette, + PastelPalette, + TableauPalette, + SPECIAL_RULE_MATCHES, +} from '@kbn/coloring'; import { ColorPicker, useDebouncedValue } from '@kbn/visualization-ui-components'; +import { EuiButtonEmpty, EuiColorPaletteDisplay, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useState, useCallback } from 'react'; +import { getColorCategories } from '@kbn/expression-xy-plugin/public'; import { PieVisualizationState } from '../../../common/types'; import { VisualizationDimensionEditorProps } from '../../types'; -import { PalettePicker } from '../../shared_components'; +import { PalettePanelContainer } from '../../shared_components'; import { CollapseSetting } from '../../shared_components/collapse_setting'; import { getDefaultColorForMultiMetricDimension, @@ -22,6 +37,7 @@ import { type DimensionEditorProps = VisualizationDimensionEditorProps & { paletteService: PaletteRegistry; + isDarkMode: boolean; }; export function DimensionEditor(props: DimensionEditorProps) { @@ -30,6 +46,7 @@ export function DimensionEditor(props: DimensionEditorProps) { value: props.state, onChange: props.setState, }); + const [isPaletteOpen, setIsPaletteOpen] = useState(false); const currentLayer = localState.layers.find((layer) => layer.layerId === props.layerId); @@ -61,6 +78,23 @@ export function DimensionEditor(props: DimensionEditorProps) { [currentLayer, localState, props.accessor, setLocalState] ); + const setColorMapping = useCallback( + (updatedColorMap: ColorMapping.Config) => { + setLocalState({ + ...localState, + layers: localState.layers.map((layer) => + layer.layerId === currentLayer?.layerId + ? { + ...layer, + colorMapping: updatedColorMap, + } + : layer + ), + }); + }, + [localState, currentLayer, setLocalState] + ); + if (!currentLayer) { return null; } @@ -84,16 +118,82 @@ export function DimensionEditor(props: DimensionEditorProps) { }) : undefined; + // TODO: move the available palette elsewhere + const availablePalettes = new Map([ + [EUIPalette.id, EUIPalette], + [TableauPalette.id, TableauPalette], + [IKEAPalette.id, IKEAPalette], + [PastelPalette.id, PastelPalette], + [NeutralPalette.id, NeutralPalette], + ]); + const colors = getPaletteColors(false, currentLayer.colorMapping); + const table = props.frame.activeData?.[currentLayer.layerId]; + const splitCategories = getColorCategories(table?.rows ?? [], props.accessor); + return ( <> {props.accessor === firstNonCollapsedColumnId && ( - { - setLocalState({ ...props.state, palette: newPalette }); - }} - /> + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + /> + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + size="xs" + flush="both" + > + {i18n.translate('xpack.lens.paletteXYGradient.customize', { + defaultMessage: 'Edit', + })} + + setIsPaletteOpen(!isPaletteOpen)} + > +
+ setColorMapping(model)} + palettes={availablePalettes} + data={{ + type: 'categories', + categories: splitCategories, + specialHandling: SPECIAL_RULE_MATCHES, + }} + /> +
+
+
+
+ // { + // setLocalState({ ...props.state, palette: newPalette }); + // }} + // /> )} {showColorPicker && ( ; + const isDarkMode = useObservable(kibanaTheme.theme$, { darkMode: false }).darkMode; + return ; }, DimensionEditorDataExtraComponent(props) { return ; From 7336f592009bcc624bd5ecb81044e537651f97ad Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 25 Aug 2023 17:23:11 +0200 Subject: [PATCH 05/71] feat: integrate color mapping with tagcloud --- .../expression_functions/tagcloud_function.ts | 8 ++ .../common/types/expression_functions.ts | 2 + .../expression_tagcloud/kibana.jsonc | 1 + .../public/components/tagcloud_component.tsx | 66 +++++++++- .../public/visualizations/tagcloud/index.ts | 2 +- .../tagcloud/tagcloud_visualization.tsx | 13 +- .../tagcloud/tags_dimension_editor.tsx | 124 ++++++++++++++++-- .../public/visualizations/tagcloud/types.ts | 3 +- 8 files changed, 199 insertions(+), 20 deletions(-) diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts index ec69431cd1735..75148e570331c 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/tagcloud_function.ts @@ -51,6 +51,9 @@ const strings = { isPreview: i18n.translate('expressionTagcloud.functions.tagcloud.args.isPreviewHelpText', { defaultMessage: 'Set isPreview to true to avoid showing out of room warnings', }), + colorMapping: i18n.translate('expressionTagcloud.layer.colorMapping.help', { + defaultMessage: 'JSON key-value pairs of the color mapping model', + }), }, dimension: { tags: i18n.translate('expressionTagcloud.functions.tagcloud.dimension.tags', { @@ -146,6 +149,10 @@ export const tagcloudFunction: ExpressionTagcloudFunction = () => { default: false, required: false, }, + colorMapping: { + types: ['string'], + help: argHelp.colorMapping, + }, }, fn(input, args, handlers) { validateAccessor(args.metric, input.columns); @@ -167,6 +174,7 @@ export const tagcloudFunction: ExpressionTagcloudFunction = () => { (handlers.variables?.embeddableTitle as string) ?? handlers.getExecutionContext?.()?.description, isPreview: Boolean(args.isPreview), + colorMapping: args.colorMapping, }; if (handlers?.inspectorAdapters?.tables) { diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts index 985da788c6ffc..73159130eb6bc 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts @@ -27,6 +27,7 @@ interface TagCloudCommonParams { metric: ExpressionValueVisDimension | string; bucket?: ExpressionValueVisDimension | string; palette: PaletteOutput; + colorMapping: string; // JSON stringified object of the color mapping } export interface TagCloudVisConfig extends TagCloudCommonParams { @@ -42,6 +43,7 @@ export interface TagcloudRendererConfig { visData: Datatable; visParams: TagCloudRendererParams; syncColors: boolean; + isDarkMode: boolean; overrides?: AllowedSettingsOverrides & AllowedChartOverrides; } diff --git a/src/plugins/chart_expressions/expression_tagcloud/kibana.jsonc b/src/plugins/chart_expressions/expression_tagcloud/kibana.jsonc index 6c6ce82d321ed..b6bf410e2786f 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/kibana.jsonc +++ b/src/plugins/chart_expressions/expression_tagcloud/kibana.jsonc @@ -8,6 +8,7 @@ "server": true, "browser": true, "requiredPlugins": [ + "data", "expressions", "visualizations", "charts", diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index adfc3df81f97f..51a477bd1361e 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -13,11 +13,21 @@ import { EuiIconTip, EuiResizeObserver } from '@elastic/eui'; import { IconChartTagcloud } from '@kbn/chart-icons'; import { Chart, Settings, Wordcloud, RenderChangeListener } from '@elastic/charts'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; -import type { PaletteRegistry, PaletteOutput } from '@kbn/coloring'; -import { IInterpreterRenderHandlers } from '@kbn/expressions-plugin/public'; +import { + PaletteRegistry, + PaletteOutput, + ColorMapping, + getColorFactory, + SPECIAL_RULE_MATCHES, + getPalette, + availablePalettes, + NeutralPalette, +} from '@kbn/coloring'; +import { IInterpreterRenderHandlers, DatatableRow } from '@kbn/expressions-plugin/public'; import { getOverridesFor } from '@kbn/chart-expressions-common'; import type { AllowedSettingsOverrides, AllowedChartOverrides } from '@kbn/charts-plugin/common'; import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { isMultiFieldKey } from '@kbn/data-plugin/common'; import { getFormatService } from '../format_service'; import { TagcloudRendererConfig } from '../../common/types'; import { ScaleOptions, Orientation } from '../../common/constants'; @@ -84,9 +94,10 @@ export const TagCloudChart = ({ renderComplete, syncColors, overrides, + isDarkMode, }: TagCloudChartProps) => { const [warning, setWarning] = useState(false); - const { bucket, metric, scale, palette, showLabel, orientation } = visParams; + const { bucket, metric, scale, palette, showLabel, orientation, colorMapping } = visParams; const bucketFormatter = useMemo(() => { return bucket @@ -96,23 +107,39 @@ export const TagCloudChart = ({ const tagCloudData = useMemo(() => { const bucketColumn = bucket ? getColumnByAccessor(bucket, visData.columns)! : null; - const tagColumn = bucket ? bucketColumn!.id : null; + const tagColumn = bucket ? bucketColumn!.id : undefined; const metricColumn = getColumnByAccessor(metric, visData.columns)!.id; const metrics = visData.rows.map((row) => row[metricColumn]); - const values = bucket && tagColumn !== null ? visData.rows.map((row) => row[tagColumn]) : []; + const values = + bucket && tagColumn !== undefined ? visData.rows.map((row) => row[tagColumn]) : []; const maxValue = Math.max(...metrics); const minValue = Math.min(...metrics); + const colorMappingModel: ColorMapping.Config = JSON.parse(colorMapping); + const splitCategories = getColorCategories(visData.rows, tagColumn); + const getColorFromMappings = getColorFactory( + colorMappingModel, + getPalette(availablePalettes, NeutralPalette), + isDarkMode, + { type: 'categories', categories: splitCategories, specialHandling: SPECIAL_RULE_MATCHES } + ); + // TODO: configure this only for new + const canUseColorMapping = true; + return visData.rows.map((row) => { - const tag = tagColumn === null ? 'all' : row[tagColumn]; + const tag = tagColumn === undefined ? 'all' : row[tagColumn]; + + const category = isMultiFieldKey(tag) ? tag.keys : tag; return { text: bucketFormatter ? bucketFormatter.convert(tag, 'text') : tag, weight: tag === 'all' || visData.rows.length <= 1 ? 1 : calculateWeight(row[metricColumn], minValue, maxValue, 0, 1) || 0, - color: getColor(palettesRegistry, palette, tag, values, syncColors) || 'rgba(0,0,0,0)', + color: canUseColorMapping + ? getColorFromMappings(category) + : getColor(palettesRegistry, palette, tag, values, syncColors) || 'rgba(0,0,0,0)', }; }); }, [ @@ -124,6 +151,8 @@ export const TagCloudChart = ({ syncColors, visData.columns, visData.rows, + colorMapping, + isDarkMode, ]); useEffect(() => { @@ -278,3 +307,26 @@ export const TagCloudChart = ({ // eslint-disable-next-line import/no-default-export export { TagCloudChart as default }; + +// TODO: export to a reusable function +export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; +export function getColorCategories(rows: DatatableRow[], accessor?: string) { + return accessor + ? rows.reduce<{ keys: Set; array: Array }>( + (acc, r) => { + const value = r[accessor]; + if (value === undefined) { + return acc; + } + const key = value.hasOwnProperty('keys') ? [...value.keys] : [value]; + const stringifiedKeys = key.join(','); + if (!acc.keys.has(stringifiedKeys)) { + acc.keys.add(stringifiedKeys); + acc.array.push(key.length === 1 ? key[0] : key); + } + return acc; + }, + { keys: new Set(), array: [] } + ).array + : []; +} diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/index.ts b/x-pack/plugins/lens/public/visualizations/tagcloud/index.ts index 129d8f4eb545f..e58f8fe673127 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/index.ts +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/index.ts @@ -19,7 +19,7 @@ export class TagcloudVisualization { editorFrame.registerVisualization(async () => { const { getTagcloudVisualization } = await import('../../async_services'); const palettes = await charts.palettes.getPalettes(); - return getTagcloudVisualization({ paletteService: palettes, theme: core.theme }); + return getTagcloudVisualization({ paletteService: palettes, kibanaTheme: core.theme }); }); } } diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx index d1bd1ec337bc0..220c4e8e4ce13 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx @@ -16,9 +16,10 @@ import { buildExpressionFunction, ExpressionFunctionTheme, } from '@kbn/expressions-plugin/common'; -import { PaletteRegistry } from '@kbn/coloring'; +import { PaletteRegistry, DEFAULT_COLOR_MAPPING_CONFIG } from '@kbn/coloring'; import { IconChartTagcloud } from '@kbn/chart-icons'; import { SystemPaletteExpressionFunctionDefinition } from '@kbn/charts-plugin/common'; +import useObservable from 'react-use/lib/useObservable'; import type { OperationMetadata, Visualization } from '../..'; import type { TagcloudState } from './types'; import { getSuggestions } from './suggestions'; @@ -31,10 +32,10 @@ const METRIC_GROUP_ID = 'metric'; export const getTagcloudVisualization = ({ paletteService, - theme, + kibanaTheme, }: { paletteService: PaletteRegistry; - theme: ThemeServiceStart; + kibanaTheme: ThemeServiceStart; }): Visualization => ({ id: 'lnsTagcloud', @@ -197,6 +198,7 @@ export const getTagcloudVisualization = ({ ), ]).toAst(), showLabel: state.showLabel, + colorMapping: JSON.stringify(state.colorMapping ?? DEFAULT_COLOR_MAPPING_CONFIG), }).toAst(), ], }; @@ -235,6 +237,7 @@ export const getTagcloudVisualization = ({ ), ]).toAst(), showLabel: false, + colorMapping: JSON.stringify(state.colorMapping ?? DEFAULT_COLOR_MAPPING_CONFIG), }).toAst(), ], }; @@ -266,12 +269,16 @@ export const getTagcloudVisualization = ({ }, DimensionEditorComponent(props) { + const isDarkMode: boolean = useObservable(kibanaTheme.theme$, { darkMode: false }).darkMode; if (props.groupId === TAG_GROUP_ID) { return ( ); } diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index e91a73982dd38..d894eac9d6b0c 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -6,24 +6,132 @@ */ import React from 'react'; -import { PaletteRegistry } from '@kbn/coloring'; +import { + PaletteRegistry, + CategoricalColorMapping, + DEFAULT_COLOR_MAPPING_CONFIG, + ColorMapping, + SPECIAL_RULE_MATCHES, + getPaletteColors, + EUIPalette, + IKEAPalette, + NeutralPalette, + PastelPalette, + TableauPalette, +} from '@kbn/coloring'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty, EuiColorPaletteDisplay, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { getColorCategories } from '@kbn/expression-xy-plugin/public'; +import { useState, MutableRefObject, useCallback } from 'react'; +import { PalettePicker } from '@kbn/coloring/src/shared_components/coloring/palette_picker'; import type { TagcloudState } from './types'; -import { PalettePicker } from '../../shared_components'; +import { PalettePanelContainer } from '../../shared_components'; +import { FramePublicAPI } from '../../types'; interface Props { paletteService: PaletteRegistry; state: TagcloudState; setState: (state: TagcloudState) => void; + frame: FramePublicAPI; + panelRef: MutableRefObject; + isDarkMode: boolean; } -export function TagsDimensionEditor(props: Props) { - return ( +export function TagsDimensionEditor({ + state, + frame, + setState, + panelRef, + isDarkMode, + paletteService, +}: Props) { + const [isPaletteOpen, setIsPaletteOpen] = useState(false); + // TODO: move the available palette elsewhere + const availablePalettes = new Map([ + [EUIPalette.id, EUIPalette], + [TableauPalette.id, TableauPalette], + [IKEAPalette.id, IKEAPalette], + [PastelPalette.id, PastelPalette], + [NeutralPalette.id, NeutralPalette], + ]); + const colors = getPaletteColors(false, state.colorMapping); + const table = frame.activeData?.[state.layerId]; + const splitCategories = getColorCategories(table?.rows ?? [], state.tagAccessor); + + const setColorMapping = useCallback( + (updatedColorMap: ColorMapping.Config) => { + setState({ + ...state, + colorMapping: { ...updatedColorMap }, + }); + }, + [state, setState] + ); + + const canUseColorMapping = true; + + return canUseColorMapping ? ( + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + /> + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + size="xs" + flush="both" + > + {i18n.translate('xpack.lens.paletteXYGradient.customize', { + defaultMessage: 'Edit', + })} + + setIsPaletteOpen(!isPaletteOpen)} + > +
+ setColorMapping(model)} + palettes={availablePalettes} + data={{ + type: 'categories', + categories: splitCategories, + specialHandling: SPECIAL_RULE_MATCHES, + }} + /> +
+
+
+
+ ) : ( { - props.setState({ - ...props.state, + setState({ + ...state, palette: newPalette, }); }} diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/types.ts b/x-pack/plugins/lens/public/visualizations/tagcloud/types.ts index c4a6ff1ddb6ad..afc83074c1fa2 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/types.ts +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/types.ts @@ -7,7 +7,7 @@ import { $Values } from '@kbn/utility-types'; import { Datatable } from '@kbn/expressions-plugin/common'; -import type { PaletteOutput } from '@kbn/coloring'; +import { PaletteOutput, ColorMapping } from '@kbn/coloring'; import { Orientation } from '@kbn/expression-tagcloud-plugin/common'; export interface TagcloudState { @@ -19,6 +19,7 @@ export interface TagcloudState { orientation: $Values; palette?: PaletteOutput; showLabel: boolean; + colorMapping?: ColorMapping.Config; } export interface TagcloudConfig extends TagcloudState { From c00284c42ab12bbcd94d79747931ba19bd0546c2 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 25 Aug 2023 17:31:43 +0200 Subject: [PATCH 06/71] feat: add color mapping panel title and badge --- .../coloring/palette_panel_container.tsx | 24 +++++++++++++------ .../datatable/components/dimension_editor.tsx | 4 ++++ .../visualizations/gauge/dimension_editor.tsx | 4 ++++ .../heatmap/dimension_editor.tsx | 4 ++++ .../legacy_metric/dimension_editor.tsx | 4 ++++ .../metric/dimension_editor.tsx | 4 ++++ .../partition/dimension_editor.tsx | 4 ++++ .../tagcloud/tags_dimension_editor.tsx | 4 ++++ .../xy/xy_config_panel/dimension_editor.tsx | 4 ++++ 9 files changed, 49 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx index b910354f1f68d..9df14e6152867 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx @@ -20,6 +20,7 @@ import { EuiFocusTrap, EuiOutsideClickDetector, EuiPortal, + EuiBetaBadge, } from '@elastic/eui'; export function PalettePanelContainer({ @@ -27,8 +28,12 @@ export function PalettePanelContainer({ handleClose, siblingRef, children, + title, + isTechPreview, }: { isOpen: boolean; + title: string; + isTechPreview: boolean; handleClose: () => void; siblingRef: MutableRefObject; children?: React.ReactElement | React.ReactElement[]; @@ -76,16 +81,21 @@ export function PalettePanelContainer({ -

- - {i18n.translate('xpack.lens.table.palettePanelTitle', { - defaultMessage: 'Color', - })} - -

+ {title} + + {isTechPreview && ( + + )} +
diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx index 09f1bc93d6779..9cf42286769fe 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx @@ -253,6 +253,10 @@ export function TableDimensionEditor( siblingRef={props.panelRef} isOpen={isPaletteOpen} handleClose={() => setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', { + defaultMessage: 'Color', + })} + isTechPreview={false} > setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', { + defaultMessage: 'Color', + })} + isTechPreview={false} > {activePalette && ( setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { + defaultMessage: 'Color assignments', + })} + isTechPreview={true} >
setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { + defaultMessage: 'Color assignments', + })} + isTechPreview={true} >
setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { + defaultMessage: 'Color assignments', + })} + isTechPreview={true} >
Date: Fri, 25 Aug 2023 15:38:51 +0000 Subject: [PATCH 07/71] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/chart_expressions/expression_tagcloud/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json b/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json index 55e81302586b8..b737dfb445f09 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json +++ b/src/plugins/chart_expressions/expression_tagcloud/tsconfig.json @@ -27,6 +27,7 @@ "@kbn/analytics", "@kbn/chart-expressions-common", "@kbn/chart-icons", + "@kbn/data-plugin", ], "exclude": [ "target/**/*", From 5ab076870be315c48308fd81d812d7467bfd341e Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 28 Aug 2023 12:34:07 +0200 Subject: [PATCH 08/71] feat: handle numeric fields as categorical ones --- .../color_mapping/color/color_handling.ts | 9 +++---- .../utils/layers/get_color_from_mappings.ts | 23 +++++++++-------- .../public/utils/layers/get_layers.ts | 25 +++++++++++++++---- .../public/components/tagcloud_component.tsx | 4 +-- .../public/helpers/color/categories.ts | 3 ++- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index b3122617748a5..7e4fd8299f73b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -7,7 +7,7 @@ */ import { scaleSequential } from 'd3-scale'; -import { interpolateHsl, piecewise } from 'd3-interpolate'; +import { interpolateLab, piecewise } from 'd3-interpolate'; import { ColorMapping } from '../config'; import { changeAlpha } from './color_math'; import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; @@ -42,7 +42,7 @@ export function getAssignmentColor( ] : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); - const colorScale = scaleSequential(piecewise(interpolateHsl, steps)); + const colorScale = scaleSequential(piecewise(interpolateLab, steps)); return colorScale(index / total); } } @@ -66,7 +66,7 @@ export function getColorFactory( getPaletteFn: ReturnType, isDarkMode: boolean, data: ColorMappingInputData -): (category: string | number | string[]) => Color { +): (category: string | string[]) => Color { const palette = getPaletteFn(model.paletteId); // generate on-the-fly assignments in auto-mode based on current data. // This simplify the code by always using assignments, even if there is no real static assigmnets @@ -93,8 +93,7 @@ export function getColorFactory( }) : []; - // TODO: check if number type is needed here - return (category: string | number | string[]) => { + return (category: string | string[]) => { if (typeof category === 'string' || Array.isArray(category)) { const nonAssignedCategoryIndex = nonAssignedCategories.indexOf(category); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts index ea0e7cf569631..ec11fc7605de1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts @@ -13,12 +13,13 @@ import { getColorFactory } from '@kbn/coloring'; import { isMultiFieldKey } from '@kbn/data-plugin/common'; import { ChartTypes } from '../../../common/types'; -function getCategoryKeys(category: string | MultiFieldKey): string | string[] { - return isMultiFieldKey(category) ? category.keys : category; +export function getCategoryKeys(category: string | MultiFieldKey): string | string[] { + return isMultiFieldKey(category) ? category.keys.map(String) : `${category}`; } /** - * Pie, donut, treemap and waffle shares the same color mechanism. + * Get the color of a specific slice/section in Pie,donut,waffle and treemap. + * These chart type shares the same color assignment mechanism. */ const getPieFillColor = ( @@ -26,7 +27,7 @@ const getPieFillColor = numOfLayers: number, getColorFn: ReturnType ): NodeColorAccessor => - (key, sortIndex, node) => { + (_key, _sortIndex, node) => { const path = node[PATH_KEY]; // the category used to color the pie/donut is at the third level of the path // first two are: small multiple and pie whole center. @@ -37,7 +38,11 @@ const getPieFillColor = }; /** - * Mosaic has a slight variation in the way color are applied + * Get the color of a section in a Mosaic chart. + * This chart has a slight variation in the way color are applied. Mosaic can represent up to 2 layers, + * described in lens as the horizontal and vertical axes. + * With a single layer the color is simply applied per each category, with 2 layer, the color is applied only + * to the category that describe a row, not by column. */ const getMosaicFillColor = ( @@ -45,10 +50,10 @@ const getMosaicFillColor = numOfLayers: number, getColorFn: ReturnType ): NodeColorAccessor => - (key, sortIndex, node) => { + (_key, _sortIndex, node) => { // Special case for 2 layer mosaic where the color is per rows and the columns are not colored if (numOfLayers === 2 && layerIndex === 0) { - // TODO: the chart or kibana background should be used instead. + // transparent color will fallback to the kibana/context background return 'rgba(0,0,0,0)'; } const path = node[PATH_KEY]; @@ -57,9 +62,7 @@ const getMosaicFillColor = // and are at fourth level of `path` when using 2 layer mosaic // first two are: small multiple and pie whole center. const category = getCategoryKeys(numOfLayers === 2 ? path[3].value : path[2].value); - const color = getColorFn(category); - // increase the lightness of the color on each layer. - return lightenColor(color, layerIndex, numOfLayers); + return getColorFn(category); }; export const getPartitionFillColor = ( diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index dc7917296ea05..1216d393e09f2 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -21,6 +21,7 @@ import { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { Datatable, DatatableRow } from '@kbn/expressions-plugin/public'; +import { isMultiFieldKey } from '@kbn/data-plugin/common'; import { getDistinctSeries } from '..'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; import { sortPredicateByType, sortPredicateSaveSourceOrder } from './sort_predicate'; @@ -30,7 +31,15 @@ import { getPartitionFillColor } from './get_color_from_mappings'; // TODO: export to a reusable function export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; -export function getColorCategories(rows: DatatableRow[], accessor?: string) { + +/** + * Get the stringified version of all the categories that needs to be colored in the chart. + * Multifield keys will return as array of string and simple fields (numeric, string) will be returned as a plain unformatted string. + */ +export function getColorCategories( + rows: DatatableRow[], + accessor?: string +): Array { return accessor ? rows.reduce<{ keys: Set; array: Array }>( (acc, r) => { @@ -38,10 +47,14 @@ export function getColorCategories(rows: DatatableRow[], accessor?: string) { if (value === undefined) { return acc; } - const key = value.hasOwnProperty('keys') ? [...value.keys] : [value]; + // The categories needs to be stringified in their unformatted version. + // We can't distinguish between a number and a string from a text input and the match should + // work with both numeric field values and string values. + const key = (isMultiFieldKey(value) ? [...value.keys] : [value]).map(String); const stringifiedKeys = key.join(','); if (!acc.keys.has(stringifiedKeys)) { acc.keys.add(stringifiedKeys); + acc.array.push(key.length === 1 ? key[0] : key); } return acc; @@ -96,11 +109,13 @@ export const getLayers = ( const distinctSeries = getDistinctSeries(rows, columns); // the mosaic configures the main categories in the second column, instead of the first - // as it happens in all the other partition types + // as it happens in all the other partition types. + // Independentely from the bucket aggregation used, the categories will always be casted + // as string to make it nicely working with a text input field, avoiding a field const splitCategories = chartType === ChartTypes.MOSAIC && columns.length === 2 - ? getColorCategories(rows, columns[1]?.id as string) - : getColorCategories(rows, columns[0]?.id as string); + ? getColorCategories(rows, columns[1]?.id) + : getColorCategories(rows, columns[0]?.id); const colorMappingModel: ColorMapping.Config = JSON.parse(visParams.colorMapping); diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 51a477bd1361e..5fc29c7125264 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -130,7 +130,7 @@ export const TagCloudChart = ({ return visData.rows.map((row) => { const tag = tagColumn === undefined ? 'all' : row[tagColumn]; - const category = isMultiFieldKey(tag) ? tag.keys : tag; + const category = isMultiFieldKey(tag) ? tag.keys.map(String) : `${tag}`; return { text: bucketFormatter ? bucketFormatter.convert(tag, 'text') : tag, weight: @@ -318,7 +318,7 @@ export function getColorCategories(rows: DatatableRow[], accessor?: string) { if (value === undefined) { return acc; } - const key = value.hasOwnProperty('keys') ? [...value.keys] : [value]; + const key = (isMultiFieldKey(value) ? [...value.keys] : [value]).map(String); const stringifiedKeys = key.join(','); if (!acc.keys.has(stringifiedKeys)) { acc.keys.add(stringifiedKeys); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts index 3f81c68b40710..84108322cf384 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts @@ -7,6 +7,7 @@ */ import { DatatableRow } from '@kbn/expressions-plugin/common'; +import { isMultiFieldKey } from '@kbn/data-plugin/common'; export function getColorCategories(rows: DatatableRow[], accessor?: string) { return accessor @@ -16,7 +17,7 @@ export function getColorCategories(rows: DatatableRow[], accessor?: string) { if (value === undefined) { return acc; } - const key = value.hasOwnProperty('keys') ? [...value.keys] : [value]; + const key = (isMultiFieldKey(value) ? [...value.keys] : [value]).map(String); const stringifiedKeys = key.join(','); if (!acc.keys.has(stringifiedKeys)) { acc.keys.add(stringifiedKeys); From 7d5be202c918ff2e6bbdcc6164d3d222eb9dc7cb Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 28 Aug 2023 13:08:26 +0200 Subject: [PATCH 09/71] feat: replace confirm dialog with EUI modal --- .../palette_selector/palette_selector.tsx | 64 +++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index 12829dcd4ea1f..2398fc3c07306 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -6,11 +6,12 @@ * Side Public License, v 1. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { EuiButtonGroup, EuiColorPalettePicker, + EuiConfirmModal, EuiFlexGroup, EuiFlexItem, EuiFormRow, @@ -91,8 +92,57 @@ export function PaletteSelector({ [getPaletteFn, model, dispatch, paletteId] ); + const [preserveModalPaletteId, setPreserveModalPaletteId] = useState(null); + + const preserveChangesModal = + preserveModalPaletteId !== null ? ( + { + if (preserveModalPaletteId) switchPaletteFn(preserveModalPaletteId, true); + setPreserveModalPaletteId(null); + }} + onConfirm={() => { + if (preserveModalPaletteId) switchPaletteFn(preserveModalPaletteId, false); + setPreserveModalPaletteId(null); + }} + confirmButtonText="Discard changes" + cancelButtonText="Preserve changes" + buttonColor="danger" + defaultFocusedButton="confirm" + > +

Switching palette will discard all your custom color changes

+
+ ) : null; + + const [colorScaleModalId, setColorScaleModalId] = useState<'gradient' | 'categorical' | null>( + null + ); + + const colorScaleModal = + colorScaleModalId !== null ? ( + { + setColorScaleModalId(null); + }} + onConfirm={() => { + if (colorScaleModalId) updateColorMode(colorScaleModalId, false); + setColorScaleModalId(null); + }} + cancelButtonText="Go back" + confirmButtonText="Discard changes" + defaultFocusedButton="confirm" + buttonColor="danger" + > +

Switching to {colorScaleModalId} mode will discard all your custom color changes

+
+ ) : null; + return ( <> + {preserveChangesModal} + {colorScaleModal} @@ -113,10 +163,7 @@ export function PaletteSelector({ model.colorMode.type === 'gradient' && model.colorMode.steps.some((a) => a.touched); if (hasChanges || hasGradientChanges) { - const preserve = window.confirm( - 'Do you want to preserve the color changes? (cancel for NO)' - ); - switchPaletteFn(selectedPaletteId, preserve); + setPreserveModalPaletteId(selectedPaletteId); } else { switchPaletteFn(selectedPaletteId, false); } @@ -150,12 +197,7 @@ export function PaletteSelector({ model.colorMode.steps.some((a) => a.touched); if (hasChanges || hasGradientChanges) { - const okToSwitch = window.confirm( - `Switch to ${id} will clear all your custom color changes, are you Ok? (cancel for NO)` - ); - if (okToSwitch) { - updateColorMode(id as 'gradient' | 'categorical', false); - } + setColorScaleModalId(id as 'gradient' | 'categorical'); } else { updateColorMode(id as 'gradient' | 'categorical', false); } From 87c7c975f52d03459906ee84be9d77088395cf56 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 28 Aug 2023 18:33:49 +0200 Subject: [PATCH 10/71] fix: add missing colorMapping usage blockers --- .../common/types/expression_functions.ts | 4 ++-- .../public/helpers/data_layers.tsx | 15 ++++++++++--- x-pack/plugins/lens/public/app_plugin/app.tsx | 2 +- .../public/visualizations/xy/to_expression.ts | 2 +- .../visualizations/xy/visualization.tsx | 21 ++++++++++++------- .../xy/xy_config_panel/dimension_editor.tsx | 2 +- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index 19e6bad146d62..a81128f6e74a7 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -136,7 +136,7 @@ export interface DataLayerArgs { isStacked: boolean; isHorizontal: boolean; palette: PaletteOutput; - colorMapping: string; // JSON stringified object of the color mapping + colorMapping?: string; // JSON stringified object of the color mapping decorations?: DataDecorationConfigResult[]; curveType?: XYCurveType; } @@ -164,7 +164,7 @@ export interface ExtendedDataLayerArgs { isStacked: boolean; isHorizontal: boolean; palette: PaletteOutput; - colorMapping: string; + colorMapping?: string; // palette will always be set on the expression decorations?: DataDecorationConfigResult[]; curveType?: XYCurveType; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 3eb08c3b790b1..078c7d17dc714 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -23,7 +23,12 @@ import type { PersistedState } from '@kbn/visualizations-plugin/public'; import { Datatable } from '@kbn/expressions-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; -import { ColorMapping, PaletteRegistry, SeriesLayer } from '@kbn/coloring'; +import { + ColorMapping, + DEFAULT_COLOR_MAPPING_CONFIG, + PaletteRegistry, + SeriesLayer, +} from '@kbn/coloring'; import { getPalette, availablePalettes, NeutralPalette } from '@kbn/coloring'; import { SPECIAL_RULE_MATCHES } from '@kbn/coloring/src/shared_components/color_mapping/color/rule_matching'; import { isDataLayer } from '../../common/utils/layer_types_guards'; @@ -490,10 +495,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({ splitColumnIds.length > 0 ? splitColumnIds[0] : undefined ); - const colorMappingModel: ColorMapping.Config = JSON.parse(layer.colorMapping); + const colorMappingModel: ColorMapping.Config = layer.colorMapping + ? JSON.parse(layer.colorMapping) + : { ...DEFAULT_COLOR_MAPPING_CONFIG }; // TODO: to be replaced by a check for new chart vs existing charts - const canUseColorMapping = true; + // if no colorMapping, check if is new chart, if so, add, if not use old + + const canUseColorMapping = layer.colorMapping ? true : false; const colorFn: SeriesColorAccessorFn = // for the MVP just apply color mapping if we have a breakdown by configured canUseColorMapping && splitColumnIds.length > 0 diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 22c5a21ad3377..d68476598ad99 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -154,7 +154,7 @@ export function App({ useExecutionContext(executionContext, { type: 'application', - id: savedObjectId || 'new', + id: savedObjectId || 'new', // TODO: this doesn't consider when lens is saved by value page: 'editor', }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 9f5bc0e9e1f53..6458d9c8505cf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -511,7 +511,7 @@ const dataLayerToExpression = ( name: 'default', }), ]).toAst(), - colorMapping: JSON.stringify(layer.colorMapping ?? DEFAULT_COLOR_MAPPING_CONFIG), + colorMapping: layer.colorMapping ? JSON.stringify(layer.colorMapping) : undefined, }); return { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 964bafd4a2083..2ced23e706fa9 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -418,14 +418,21 @@ export const getXyVisualization = ({ } ).length < 2; + const canUseColorMapping = layer.colorMapping ? true : false; let colors: string[] = []; - kibanaTheme.theme$ - .subscribe({ - next(theme) { - colors = getPaletteColors(theme.darkMode, layer.colorMapping); - }, - }) - .unsubscribe(); + if (canUseColorMapping) { + kibanaTheme.theme$ + .subscribe({ + next(theme) { + colors = getPaletteColors(theme.darkMode, layer.colorMapping); + }, + }) + .unsubscribe(); + } else { + colors = paletteService + .get(dataLayer.palette?.name || 'default') + .getCategoricalColors(10, dataLayer.palette?.params); + } return { groups: [ diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 762347b234534..51ff8bb1c11e5 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -153,7 +153,7 @@ export function DataDimensionEditor( const { splitAccessor } = layer; const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); - const canUseColorMapping = true; + const canUseColorMapping = layer.colorMapping ? true : false; if (props.groupId === 'breakdown' && !layer.collapseFn) { return canUseColorMapping ? ( Date: Mon, 28 Aug 2023 16:39:32 +0000 Subject: [PATCH 11/71] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- x-pack/plugins/lens/public/visualizations/xy/to_expression.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts index 6458d9c8505cf..2b80a39fc3b53 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/to_expression.ts @@ -7,7 +7,7 @@ import { Ast } from '@kbn/interpreter'; import { Position, ScaleType } from '@elastic/charts'; -import { DEFAULT_COLOR_MAPPING_CONFIG, PaletteRegistry } from '@kbn/coloring'; +import { PaletteRegistry } from '@kbn/coloring'; import { buildExpression, buildExpressionFunction, From 733501d0604c29d269fe962a365a0dadac96a2d4 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 31 Aug 2023 12:17:42 +0200 Subject: [PATCH 12/71] feat: switch legacy/new color mapping --- .../categorical_color_mapping.tsx | 2 +- .../color_mapping/color/rule_matching.ts | 3 +- .../components/assignment/assignment.tsx | 2 +- .../components/assignment/match.tsx | 9 +- .../components/container/container.tsx | 47 ++++---- .../palette_selector/palette_selector.tsx | 7 +- .../shared_components/color_mapping/index.ts | 2 +- .../color_categories.ts} | 20 +++- src/plugins/chart_expressions/common/index.ts | 1 + .../common/types/expression_renderers.ts | 2 +- .../color_mapping_accessors.ts} | 0 .../public/utils/layers/get_layers.ts | 106 ++++++++---------- .../common/types/expression_functions.ts | 2 +- .../public/components/tagcloud_component.tsx | 68 +++++------ .../public/helpers/color/color_mapping.ts | 40 ------- .../helpers/color/color_mapping_accessor.ts | 50 +++++++++ .../public/helpers/data_layers.tsx | 40 ++----- .../data/common/search/aggs/buckets/index.ts | 2 +- .../search/aggs/buckets/multi_field_key.ts | 6 + .../coloring/palette_panel_container.tsx | 12 -- .../shared_components/palette_picker.tsx | 32 +++--- .../datatable/components/dimension_editor.tsx | 1 - .../visualizations/gauge/dimension_editor.tsx | 1 - .../heatmap/dimension_editor.tsx | 1 - .../partition/dimension_editor.tsx | 81 ++++++++----- .../visualizations/partition/to_expression.ts | 4 +- .../partition/visualization.tsx | 3 +- .../tagcloud/tagcloud_visualization.tsx | 5 +- .../tagcloud/tags_dimension_editor.tsx | 104 +++++++++++------ .../visualizations/xy/visualization.tsx | 8 +- .../xy/xy_config_panel/dimension_editor.tsx | 77 ++++++++----- 31 files changed, 409 insertions(+), 329 deletions(-) rename src/plugins/chart_expressions/{expression_xy/public/helpers/color/categories.ts => common/color_categories.ts} (53%) rename src/plugins/chart_expressions/expression_partition_vis/public/utils/{layers/get_color_from_mappings.ts => colors/color_mapping_accessors.ts} (100%) delete mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts create mode 100644 src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx index 847d48a9fb835..b3ea219204d33 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx @@ -20,7 +20,7 @@ export type ColorMappingInputData = type: 'categories'; /** an ORDERED array of categories rendered in the visualization */ categories: Array; - specialHandling: Map; + specialTokens: Map; } | { type: 'ranges'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts index 4b7a1630f88ab..7557644154a52 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/rule_matching.ts @@ -39,7 +39,8 @@ export function rangeMatch(rule: ColorMapping.RuleRange, value: number) { ); } -export const SPECIAL_RULE_MATCHES = new Map([ +// TODO: move in some data/table related package +export const SPECIAL_TOKENS_STRING_CONVERTION = new Map([ ['__other__', 'Other'], ['', '(empty)'], ]); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index ce98b94753a52..dc78c695212ce 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -75,7 +75,7 @@ export function Assignment({ index={index} rule={assignment.rule} options={data.type === 'categories' ? data.categories : []} - specialHandling={data.type === 'categories' ? data.specialHandling : new Map()} + specialTokens={data.type === 'categories' ? data.specialTokens : new Map()} updateValue={(values: Array) => { dispatch( updateAssignmentRule({ diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index c42c1333e3b54..a480913ad1959 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { EuiComboBox, EuiFlexItem } from '@elastic/eui'; import { ColorMapping } from '../../config'; +// TODO: move this outside or configurable export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; export const Match: React.FC<{ @@ -21,8 +22,8 @@ export const Match: React.FC<{ | ColorMapping.RuleRegExp; updateValue: (values: Array) => void; options: Array; - specialHandling: Map; -}> = ({ rule, updateValue, editable, options, specialHandling }) => { + specialTokens: Map; +}> = ({ rule, updateValue, editable, options, specialTokens }) => { const selectedOptions = rule.type === 'auto' ? [] @@ -32,7 +33,7 @@ export const Match: React.FC<{ const ruleValues = Array.isArray(value) ? value : [value]; return { label: ruleValues - .map((v) => specialHandling.get(v) ?? v) + .map((v) => specialTokens.get(v) ?? v) .join(MULTI_FIELD_VALUES_SEPARATOR), value, }; @@ -41,7 +42,7 @@ export const Match: React.FC<{ const convertedOptions = options.map((value) => { const ruleValues = Array.isArray(value) ? value : [value]; return { - label: ruleValues.map((v) => specialHandling.get(v) ?? v).join(MULTI_FIELD_VALUES_SEPARATOR), + label: ruleValues.map((v) => specialTokens.get(v) ?? v).join(MULTI_FIELD_VALUES_SEPARATOR), value, }; }); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index d5e59a16422a6..759201e2bb3c1 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -73,32 +73,29 @@ export function Container(props: { return ( - - - - - Assignments - - - { - if (autoAssignmentMode) { - dispatch(assignStatically(assignments)); - } else { - dispatch(assignAutomatically()); - } - }} - /> - - + + + + Assignments + + + { + if (autoAssignmentMode) { + dispatch(assignStatically(assignments)); + } else { + dispatch(assignAutomatically()); + } + }} + /> diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index 2398fc3c07306..37d42e9efeddd 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -181,14 +181,14 @@ export function PaletteSelector({ { id: `categorical`, label: 'Categorical', - iconType: 'spaces', }, { id: `gradient`, - label: 'gradient', - iconType: 'heatmap', + label: 'Gradient', }, ]} + isFullWidth + buttonSize="compressed" idSelected={colorMode.type} onChange={(id) => { const hasChanges = model.assignments.some((a) => a.touched); @@ -202,7 +202,6 @@ export function PaletteSelector({ updateColorMode(id as 'gradient' | 'categorical', false); } }} - isIconOnly /> diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts index 2c084e2f8e185..4b16cfadf891d 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts @@ -11,5 +11,5 @@ export type { ColorMappingInputData } from './categorical_color_mapping'; export type { ColorMapping } from './config'; export * from './palettes/default_palettes'; export * from './color/color_handling'; -export { SPECIAL_RULE_MATCHES } from './color/rule_matching'; +export { SPECIAL_TOKENS_STRING_CONVERTION } from './color/rule_matching'; export { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors } from './config/default_color_mapping'; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts b/src/plugins/chart_expressions/common/color_categories.ts similarity index 53% rename from src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts rename to src/plugins/chart_expressions/common/color_categories.ts index 84108322cf384..0bb8811f2701a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color/categories.ts +++ b/src/plugins/chart_expressions/common/color_categories.ts @@ -9,23 +9,33 @@ import { DatatableRow } from '@kbn/expressions-plugin/common'; import { isMultiFieldKey } from '@kbn/data-plugin/common'; -export function getColorCategories(rows: DatatableRow[], accessor?: string) { +/** + * Get the stringified version of all the categories that needs to be colored in the chart. + * Multifield keys will return as array of string and simple fields (numeric, string) will be returned as a plain unformatted string. + */ +export function getColorCategories( + rows: DatatableRow[], + accessor?: string +): Array { return accessor - ? rows.reduce<{ keys: Set; array: Array }>( + ? rows.reduce<{ keys: Set; categories: Array }>( (acc, r) => { const value = r[accessor]; if (value === undefined) { return acc; } + // The categories needs to be stringified in their unformatted version. + // We can't distinguish between a number and a string from a text input and the match should + // work with both numeric field values and string values. const key = (isMultiFieldKey(value) ? [...value.keys] : [value]).map(String); const stringifiedKeys = key.join(','); if (!acc.keys.has(stringifiedKeys)) { acc.keys.add(stringifiedKeys); - acc.array.push(key.length === 1 ? key[0] : key); + acc.categories.push(key.length === 1 ? key[0] : key); } return acc; }, - { keys: new Set(), array: [] } - ).array + { keys: new Set(), categories: [] } + ).categories : []; } diff --git a/src/plugins/chart_expressions/common/index.ts b/src/plugins/chart_expressions/common/index.ts index 0983b1ed28d4d..acc3b4d8c88cd 100644 --- a/src/plugins/chart_expressions/common/index.ts +++ b/src/plugins/chart_expressions/common/index.ts @@ -13,3 +13,4 @@ export { isOnAggBasedEditor, } from './utils'; export type { Simplify, MakeOverridesSerializable } from './types'; +export { getColorCategories } from './color_categories'; diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 18c8d8a7969b7..00667ace39576 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -61,7 +61,7 @@ interface VisCommonParams { maxLegendLines: number; legendSize?: LegendSize; ariaLabel?: string; - colorMapping: string; // JSON stringified object of the color mapping + colorMapping?: string; // JSON stringified object of the color mapping } interface VisCommonConfig extends VisCommonParams { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/colors/color_mapping_accessors.ts similarity index 100% rename from src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_color_from_mappings.ts rename to src/plugins/chart_expressions/expression_partition_vis/public/utils/colors/color_mapping_accessors.ts diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index 1216d393e09f2..677018b023656 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -8,10 +8,9 @@ import { Datum, PartitionLayer } from '@elastic/charts'; import { - ColorMapping, PaletteRegistry, getColorFactory, - SPECIAL_RULE_MATCHES, + SPECIAL_TOKENS_STRING_CONVERTION, getPalette, availablePalettes, NeutralPalette, @@ -21,48 +20,14 @@ import { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { Datatable, DatatableRow } from '@kbn/expressions-plugin/public'; -import { isMultiFieldKey } from '@kbn/data-plugin/common'; +import { getColorCategories } from '@kbn/chart-expressions-common'; +import { Color } from '@kbn/coloring/src/shared_components/color_mapping/color/color_handling'; import { getDistinctSeries } from '..'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; import { sortPredicateByType, sortPredicateSaveSourceOrder } from './sort_predicate'; import { byDataColorPaletteMap, getColor } from './get_color'; import { getNodeLabel } from './get_node_labels'; -import { getPartitionFillColor } from './get_color_from_mappings'; - -// TODO: export to a reusable function -export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; - -/** - * Get the stringified version of all the categories that needs to be colored in the chart. - * Multifield keys will return as array of string and simple fields (numeric, string) will be returned as a plain unformatted string. - */ -export function getColorCategories( - rows: DatatableRow[], - accessor?: string -): Array { - return accessor - ? rows.reduce<{ keys: Set; array: Array }>( - (acc, r) => { - const value = r[accessor]; - if (value === undefined) { - return acc; - } - // The categories needs to be stringified in their unformatted version. - // We can't distinguish between a number and a string from a text input and the match should - // work with both numeric field values and string values. - const key = (isMultiFieldKey(value) ? [...value.keys] : [value]).map(String); - const stringifiedKeys = key.join(','); - if (!acc.keys.has(stringifiedKeys)) { - acc.keys.add(stringifiedKeys); - - acc.array.push(key.length === 1 ? key[0] : key); - } - return acc; - }, - { keys: new Set(), array: [] } - ).array - : []; -} +import { getPartitionFillColor } from '../colors/color_mapping_accessors'; // This is particularly useful in case of a text based languages where // it's no possible to use a missingBucketLabel @@ -108,26 +73,15 @@ export const getLayers = ( const distinctSeries = getDistinctSeries(rows, columns); - // the mosaic configures the main categories in the second column, instead of the first - // as it happens in all the other partition types. - // Independentely from the bucket aggregation used, the categories will always be casted - // as string to make it nicely working with a text input field, avoiding a field - const splitCategories = - chartType === ChartTypes.MOSAIC && columns.length === 2 - ? getColorCategories(rows, columns[1]?.id) - : getColorCategories(rows, columns[0]?.id); - - const colorMappingModel: ColorMapping.Config = JSON.parse(visParams.colorMapping); - - const getColorFromMappings = getColorFactory( - colorMappingModel, - getPalette(availablePalettes, NeutralPalette), + // return a fn only if color mapping is available in visParams + const getColorFromMappingFn = getColorFromMappingFactory( + chartType, + columns, + rows, isDarkMode, - { type: 'categories', categories: splitCategories, specialHandling: SPECIAL_RULE_MATCHES } + visParams.colorMapping ); - const canUseColorMappings = true; - return columns.map((col, layerIndex) => { return { groupByRollup: (d: Datum) => (col.id ? d[col.id] ?? emptySliceLabel : col.name), @@ -141,8 +95,9 @@ export const getLayers = ( ? sortPredicateSaveSourceOrder() : sortPredicateForType, shape: { - fillColor: canUseColorMappings - ? getPartitionFillColor(chartType, layerIndex, columns.length, getColorFromMappings) + // this applies color mapping only if visParams.colorMapping is available + fillColor: getColorFromMappingFn + ? getPartitionFillColor(chartType, layerIndex, columns.length, getColorFromMappingFn) : (key, sortIndex, node) => getColor( chartType, @@ -166,3 +121,38 @@ export const getLayers = ( }; }); }; + +/** + * If colorMapping is available, returns a function that accept a string or an array of strings (used in case of multi-field-key) + * and returns a color specified in the provided mapping + */ +function getColorFromMappingFactory( + chartType: ChartTypes, + columns: Array>, + rows: DatatableRow[], + isDarkMode: boolean, + colorMapping?: string +): undefined | ((category: string | string[]) => Color) { + if (!colorMapping) { + // return undefined, we will use the legacy color mapping instead + return undefined; + } + // the mosaic configures the main categories in the second column, instead of the first + // as it happens in all the other partition types. + // Independentely from the bucket aggregation used, the categories will always be casted + // as string to make it nicely working with a text input field, avoiding a field + const categories = + chartType === ChartTypes.MOSAIC && columns.length === 2 + ? getColorCategories(rows, columns[1]?.id) + : getColorCategories(rows, columns[0]?.id); + return getColorFactory( + JSON.parse(colorMapping), + getPalette(availablePalettes, NeutralPalette), + isDarkMode, + { + type: 'categories', + categories, + specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, + } + ); +} diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts index 73159130eb6bc..f1c5d40ca430e 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts @@ -27,7 +27,7 @@ interface TagCloudCommonParams { metric: ExpressionValueVisDimension | string; bucket?: ExpressionValueVisDimension | string; palette: PaletteOutput; - colorMapping: string; // JSON stringified object of the color mapping + colorMapping?: string; // JSON stringified object of the color mapping } export interface TagCloudVisConfig extends TagCloudCommonParams { diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 5fc29c7125264..adecb353a70cf 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -16,18 +16,18 @@ import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { PaletteRegistry, PaletteOutput, - ColorMapping, getColorFactory, - SPECIAL_RULE_MATCHES, getPalette, availablePalettes, NeutralPalette, + SPECIAL_TOKENS_STRING_CONVERTION, } from '@kbn/coloring'; import { IInterpreterRenderHandlers, DatatableRow } from '@kbn/expressions-plugin/public'; -import { getOverridesFor } from '@kbn/chart-expressions-common'; +import { getColorCategories, getOverridesFor } from '@kbn/chart-expressions-common'; import type { AllowedSettingsOverrides, AllowedChartOverrides } from '@kbn/charts-plugin/common'; import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; import { isMultiFieldKey } from '@kbn/data-plugin/common'; +import { Color } from '@kbn/coloring/src/shared_components/color_mapping/color/color_handling'; import { getFormatService } from '../format_service'; import { TagcloudRendererConfig } from '../../common/types'; import { ScaleOptions, Orientation } from '../../common/constants'; @@ -116,16 +116,13 @@ export const TagCloudChart = ({ const maxValue = Math.max(...metrics); const minValue = Math.min(...metrics); - const colorMappingModel: ColorMapping.Config = JSON.parse(colorMapping); - const splitCategories = getColorCategories(visData.rows, tagColumn); - const getColorFromMappings = getColorFactory( - colorMappingModel, - getPalette(availablePalettes, NeutralPalette), + const colorFromMappingFn = getColorFromMappingFactory( + tagColumn, + visData.rows, isDarkMode, - { type: 'categories', categories: splitCategories, specialHandling: SPECIAL_RULE_MATCHES } + colorMapping ); - // TODO: configure this only for new - const canUseColorMapping = true; + console.log(`has color mappaing`, colorMapping, colorFromMappingFn); return visData.rows.map((row) => { const tag = tagColumn === undefined ? 'all' : row[tagColumn]; @@ -137,8 +134,8 @@ export const TagCloudChart = ({ tag === 'all' || visData.rows.length <= 1 ? 1 : calculateWeight(row[metricColumn], minValue, maxValue, 0, 1) || 0, - color: canUseColorMapping - ? getColorFromMappings(category) + color: colorFromMappingFn + ? colorFromMappingFn(category) : getColor(palettesRegistry, palette, tag, values, syncColors) || 'rgba(0,0,0,0)', }; }); @@ -308,25 +305,28 @@ export const TagCloudChart = ({ // eslint-disable-next-line import/no-default-export export { TagCloudChart as default }; -// TODO: export to a reusable function -export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; -export function getColorCategories(rows: DatatableRow[], accessor?: string) { - return accessor - ? rows.reduce<{ keys: Set; array: Array }>( - (acc, r) => { - const value = r[accessor]; - if (value === undefined) { - return acc; - } - const key = (isMultiFieldKey(value) ? [...value.keys] : [value]).map(String); - const stringifiedKeys = key.join(','); - if (!acc.keys.has(stringifiedKeys)) { - acc.keys.add(stringifiedKeys); - acc.array.push(key.length === 1 ? key[0] : key); - } - return acc; - }, - { keys: new Set(), array: [] } - ).array - : []; +/** + * If colorMapping is available, returns a function that accept a string or an array of strings (used in case of multi-field-key) + * and returns a color specified in the provided mapping + */ +function getColorFromMappingFactory( + tagColumn: string | undefined, + rows: DatatableRow[], + isDarkMode: boolean, + colorMapping?: string +): undefined | ((category: string | string[]) => Color) { + if (!colorMapping) { + // return undefined, we will use the legacy color mapping instead + return undefined; + } + return getColorFactory( + JSON.parse(colorMapping), + getPalette(availablePalettes, NeutralPalette), + isDarkMode, + { + type: 'categories', + categories: getColorCategories(rows, tagColumn), + specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, + } + ); } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts deleted file mode 100644 index 0c589bbeead51..0000000000000 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { SeriesColorAccessorFn } from '@elastic/charts'; -import { getColorFactory, type ColorMapping, type ColorMappingInputData } from '@kbn/coloring'; -export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; -export function getColorSeriesAccessorFn( - model: ColorMapping.Config, - getPaletteFn: (paletteId: string) => ColorMapping.CategoricalPalette, - isDarkMode: boolean, - mappingData: ColorMappingInputData, - fieldId: string -): SeriesColorAccessorFn { - const specialHandlingInverse: Map = - mappingData.type === 'categories' - ? new Map([...mappingData.specialHandling.entries()].map((d) => [d[1], d[0]])) - : new Map(); - const getColor = getColorFactory(model, getPaletteFn, isDarkMode, mappingData); - return ({ splitAccessors }) => { - const category = splitAccessors.get(fieldId); - if (category === undefined) { - return 'red'; - } - const stringCategory = `${category}`; - // if the separator exist, we are de-constructing a multifieldkey into values. - const categoryForColor = stringCategory.includes(MULTI_FIELD_VALUES_SEPARATOR) - ? stringCategory.split(MULTI_FIELD_VALUES_SEPARATOR).map((c) => { - const trimmedValue = c.trim(); - return specialHandlingInverse.get(trimmedValue) ?? trimmedValue; - }) - : specialHandlingInverse.get(stringCategory) ?? stringCategory; - - return getColor(categoryForColor); - }; -} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts new file mode 100644 index 0000000000000..acc6cf817d559 --- /dev/null +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SeriesColorAccessorFn } from '@elastic/charts'; +import { getColorFactory, type ColorMapping, type ColorMappingInputData } from '@kbn/coloring'; +import { MULTI_FIELD_KEY_SEPARATOR } from '@kbn/data-plugin/common'; + +/** + * Return a color accessor function for XY charts depending on the split accessors received. + */ +export function getColorSeriesAccessorFn( + config: ColorMapping.Config, + getPaletteFn: (paletteId: string) => ColorMapping.CategoricalPalette, + isDarkMode: boolean, + mappingData: ColorMappingInputData, + fieldId: string +): SeriesColorAccessorFn { + // inverse map to handle the conversion between the formatted string and their original format + // for any specified special tokens + const specialHandlingInverseMap: Map = + mappingData.type === 'categories' + ? new Map([...mappingData.specialTokens.entries()].map((d) => [d[1], d[0]])) + : new Map(); + + const getColor = getColorFactory(config, getPaletteFn, isDarkMode, mappingData); + + return ({ splitAccessors }) => { + const splitValue = splitAccessors.get(fieldId); + // if there isn't a category associated in the split accessor, let's use the default color + if (splitValue === undefined) { + return null; + } + // category can be also a number, range, ip, multi-field. We need to stringify it to + // be sure we + const category = `${splitValue}`; + // if the separator exist, we de-construct it into a multifieldkey into values. + const categories = category.split(MULTI_FIELD_KEY_SEPARATOR).map((c) => { + const trimmedCategory = c.trim(); + return specialHandlingInverseMap.get(trimmedCategory) ?? trimmedCategory; + }); + // we must keep the array nature of a multi-field key or just use a single string + // This is required because the rule stored are checked differently for single values or multi-values + return getColor(categories.length > 1 ? categories : categories[0]); + }; +} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 078c7d17dc714..466c49b10efeb 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -23,14 +23,10 @@ import type { PersistedState } from '@kbn/visualizations-plugin/public'; import { Datatable } from '@kbn/expressions-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; -import { - ColorMapping, - DEFAULT_COLOR_MAPPING_CONFIG, - PaletteRegistry, - SeriesLayer, -} from '@kbn/coloring'; +import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; import { getPalette, availablePalettes, NeutralPalette } from '@kbn/coloring'; -import { SPECIAL_RULE_MATCHES } from '@kbn/coloring/src/shared_components/color_mapping/color/rule_matching'; +import { SPECIAL_TOKENS_STRING_CONVERTION } from '@kbn/coloring/src/shared_components/color_mapping/color/rule_matching'; +import { getColorCategories } from '@kbn/chart-expressions-common'; import { isDataLayer } from '../../common/utils/layer_types_guards'; import { CommonXYDataLayerConfig, CommonXYLayerConfig, XScaleType } from '../../common'; import { AxisModes, SeriesTypes } from '../../common/constants'; @@ -40,8 +36,7 @@ import { ColorAssignments } from './color_assignment'; import { GroupsConfiguration } from './axes_configuration'; import { LayerAccessorsTitles, LayerFieldFormats, LayersFieldFormats } from './layers'; import { getFormat } from './format'; -import { getColorSeriesAccessorFn } from './color/color_mapping'; -import { getColorCategories } from './color/categories'; +import { getColorSeriesAccessorFn } from './color/color_mapping_accessor'; type SeriesSpec = LineSeriesProps & BarSeriesProps & AreaSeriesProps; @@ -490,30 +485,17 @@ export const getSeriesProps: GetSeriesPropsFn = ({ ); }; - const splitCategories = getColorCategories( - table.rows, - splitColumnIds.length > 0 ? splitColumnIds[0] : undefined - ); - - const colorMappingModel: ColorMapping.Config = layer.colorMapping - ? JSON.parse(layer.colorMapping) - : { ...DEFAULT_COLOR_MAPPING_CONFIG }; - - // TODO: to be replaced by a check for new chart vs existing charts - // if no colorMapping, check if is new chart, if so, add, if not use old - - const canUseColorMapping = layer.colorMapping ? true : false; - const colorFn: SeriesColorAccessorFn = - // for the MVP just apply color mapping if we have a breakdown by configured - canUseColorMapping && splitColumnIds.length > 0 + const colorAccessorFn: SeriesColorAccessorFn = + // if colorMapping exist then we can apply it, if not let's use the legacy coloring method + layer.colorMapping && splitColumnIds.length > 0 ? getColorSeriesAccessorFn( - colorMappingModel, + JSON.parse(layer.colorMapping), // the color mapping is at this point just a strinfigied JSON getPalette(availablePalettes, NeutralPalette), isDarkMode, { type: 'categories', - categories: splitCategories, - specialHandling: SPECIAL_RULE_MATCHES, + categories: getColorCategories(table.rows, splitColumnIds[0]), + specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, }, splitColumnIds[0] ) @@ -550,7 +532,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ formatter?.id === 'bytes' && scaleType === ScaleType.Linear ? ScaleType.LinearBinary : scaleType, - color: colorFn, + color: colorAccessorFn, groupId: yAxis?.groupId, enableHistogramMode, stackMode, diff --git a/src/plugins/data/common/search/aggs/buckets/index.ts b/src/plugins/data/common/search/aggs/buckets/index.ts index 1d765190d1bdb..369e56caf1859 100644 --- a/src/plugins/data/common/search/aggs/buckets/index.ts +++ b/src/plugins/data/common/search/aggs/buckets/index.ts @@ -37,7 +37,7 @@ export * from './significant_text_fn'; export * from './significant_text'; export * from './terms_fn'; export * from './terms'; -export { MultiFieldKey, isMultiFieldKey } from './multi_field_key'; +export { MultiFieldKey, isMultiFieldKey, MULTI_FIELD_KEY_SEPARATOR } from './multi_field_key'; export * from './multi_terms_fn'; export * from './multi_terms'; export * from './rare_terms_fn'; diff --git a/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts b/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts index dba06e7d3683f..5b02d0d8827f2 100644 --- a/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts +++ b/src/plugins/data/common/search/aggs/buckets/multi_field_key.ts @@ -42,3 +42,9 @@ export class MultiFieldKey { export function isMultiFieldKey(field: unknown): field is MultiFieldKey { return field instanceof MultiFieldKey; } + +/** + * Multi-field key separator used in Visualizations (Lens, AggBased, TSVB). + * This differs from the separator used in the toString method of the MultiFieldKey + */ +export const MULTI_FIELD_KEY_SEPARATOR = ' › '; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx index 9df14e6152867..523c98c9d6903 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_panel_container.tsx @@ -20,7 +20,6 @@ import { EuiFocusTrap, EuiOutsideClickDetector, EuiPortal, - EuiBetaBadge, } from '@elastic/eui'; export function PalettePanelContainer({ @@ -29,11 +28,9 @@ export function PalettePanelContainer({ siblingRef, children, title, - isTechPreview, }: { isOpen: boolean; title: string; - isTechPreview: boolean; handleClose: () => void; siblingRef: MutableRefObject; children?: React.ReactElement | React.ReactElement[]; @@ -86,15 +83,6 @@ export function PalettePanelContainer({ className="lnsPalettePanelContainer__headerTitle" > {title} - - {isTechPreview && ( - - )} diff --git a/x-pack/plugins/lens/public/shared_components/palette_picker.tsx b/x-pack/plugins/lens/public/shared_components/palette_picker.tsx index efd1caba7e4da..51977e551128e 100644 --- a/x-pack/plugins/lens/public/shared_components/palette_picker.tsx +++ b/x-pack/plugins/lens/public/shared_components/palette_picker.tsx @@ -36,28 +36,24 @@ export function PalettePicker({ }); return ( - <> - { - setPalette({ - type: 'palette', - name: newPalette, - }); - }} - valueOfSelected={activePalette?.name || 'default'} - selectionDisplay={'palette'} - /> - + { + setPalette({ + type: 'palette', + name: newPalette, + }); + }} + valueOfSelected={activePalette?.name || 'default'} + selectionDisplay={'palette'} + /> ); } diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx index 9cf42286769fe..60646bdb8d054 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/dimension_editor.tsx @@ -256,7 +256,6 @@ export function TableDimensionEditor( title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', { defaultMessage: 'Color', })} - isTechPreview={false} > {activePalette && ( layer.layerId === props.layerId); - const setConfig = React.useCallback( + const canUseColorMapping = currentLayer && currentLayer.colorMapping ? true : false; + const [useNewColorMapping, setUseNewColorMapping] = useState(canUseColorMapping); + + const setConfig = useCallback( ({ color }) => { if (!currentLayer) { return; @@ -79,14 +88,14 @@ export function DimensionEditor(props: DimensionEditorProps) { ); const setColorMapping = useCallback( - (updatedColorMap: ColorMapping.Config) => { + (colorMapping?: ColorMapping.Config) => { setLocalState({ ...localState, layers: localState.layers.map((layer) => layer.layerId === currentLayer?.layerId ? { ...layer, - colorMapping: updatedColorMap, + colorMapping, } : layer ), @@ -171,34 +180,54 @@ export function DimensionEditor(props: DimensionEditorProps) { isOpen={isPaletteOpen} handleClose={() => setIsPaletteOpen(!isPaletteOpen)} title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { - defaultMessage: 'Color assignments', + defaultMessage: 'Color', })} - isTechPreview={true} >
- setColorMapping(model)} - palettes={availablePalettes} - data={{ - type: 'categories', - categories: splitCategories, - specialHandling: SPECIAL_RULE_MATCHES, - }} - /> + + + { + setColorMapping( + e.target.checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + ); + setUseNewColorMapping(e.target.checked); + }} + /> + + + {canUseColorMapping || useNewColorMapping ? ( + setColorMapping(model)} + palettes={availablePalettes} + data={{ + type: 'categories', + categories: splitCategories, + specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, + }} + /> + ) : ( + { + setLocalState({ ...props.state, palette: newPalette }); + }} + /> + )} + +
- // { - // setLocalState({ ...props.state, palette: newPalette }); - // }} - // /> )} + {/* TODO: understand how this works */} {showColorPicker && ( ({ + value: state, + onChange: setState, + }); const [isPaletteOpen, setIsPaletteOpen] = useState(false); + const [useNewColorMapping, setUseNewColorMapping] = useState(state.colorMapping ? true : false); // TODO: move the available palette elsewhere const availablePalettes = new Map([ [EUIPalette.id, EUIPalette], @@ -59,18 +73,29 @@ export function TagsDimensionEditor({ const splitCategories = getColorCategories(table?.rows ?? [], state.tagAccessor); const setColorMapping = useCallback( - (updatedColorMap: ColorMapping.Config) => { - setState({ - ...state, - colorMapping: { ...updatedColorMap }, + (colorMapping?: ColorMapping.Config) => { + setLocalState({ + ...localState, + colorMapping, + }); + }, + [localState, setLocalState] + ); + + const setPalette = useCallback( + (palette: PaletteOutput) => { + setLocalState({ + ...localState, + palette, + colorMapping: undefined, }); }, - [state, setState] + [localState, setLocalState] ); - const canUseColorMapping = true; + const canUseColorMapping = state.colorMapping; - return canUseColorMapping ? ( + return (
- setColorMapping(model)} - palettes={availablePalettes} - data={{ - type: 'categories', - categories: splitCategories, - specialHandling: SPECIAL_RULE_MATCHES, - }} - /> + + + { + setColorMapping( + e.target.checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + ); + setUseNewColorMapping(e.target.checked); + }} + /> + + + {canUseColorMapping || useNewColorMapping ? ( + setColorMapping(model)} + palettes={availablePalettes} + data={{ + type: 'categories', + categories: splitCategories, + specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, + }} + /> + ) : ( + { + setPalette(newPalette); + }} + /> + )} + +
- ) : ( - { - setState({ - ...state, - palette: newPalette, - }); - }} - /> ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 2ced23e706fa9..e833272b30db1 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Position } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { PaletteRegistry } from '@kbn/coloring'; +import type { PaletteRegistry } from '@kbn/coloring'; import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { CoreStart, SavedObjectReference, ThemeServiceStart } from '@kbn/core/public'; @@ -25,7 +25,10 @@ import type { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; import { isEqual } from 'lodash'; import { type AccessorConfig, DimensionTrigger } from '@kbn/visualization-ui-components'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { getPaletteColors } from '@kbn/coloring/src/shared_components/color_mapping/config/default_color_mapping'; +import { + DEFAULT_COLOR_MAPPING_CONFIG, + getPaletteColors, +} from '@kbn/coloring/src/shared_components/color_mapping/config/default_color_mapping'; import useObservable from 'react-use/lib/useObservable'; import { generateId } from '../../id_generator'; import { @@ -278,6 +281,7 @@ export const getXyVisualization = ({ seriesType: defaultSeriesType, showGridlines: false, layerType: LayerTypes.DATA, + colorMapping: { ...DEFAULT_COLOR_MAPPING_CONFIG }, }, ], } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 51ff8bb1c11e5..1b2d6ba8f74cf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -17,6 +17,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormRow, + EuiSwitch, htmlIdGenerator, } from '@elastic/eui'; import { @@ -30,7 +31,8 @@ import { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors, CategoricalColorMapping, - SPECIAL_RULE_MATCHES, + PaletteOutput, + SPECIAL_TOKENS_STRING_CONVERTION, } from '@kbn/coloring'; import { getColorCategories } from '@kbn/expression-xy-plugin/public'; @@ -72,8 +74,10 @@ export function DataDimensionEditor( const { state, layerId, accessor, darkMode } = props; const index = state.layers.findIndex((l) => l.layerId === layerId); const layer = state.layers[index] as XYDataLayerConfig; + const canUseColorMapping = layer.colorMapping ? true : false; const [isPaletteOpen, setIsPaletteOpen] = useState(false); + const [useNewColorMapping, setUseNewColorMapping] = useState(canUseColorMapping); const { inputValue: localState, handleInputChange: setLocalState } = useDebouncedValue({ value: props.state, @@ -106,11 +110,17 @@ export function DataDimensionEditor( ); const setColorMapping = useCallback( - (colorMapping: ColorMapping.Config) => { + (colorMapping?: ColorMapping.Config) => { setLocalState(updateLayer(localState, { ...layer, colorMapping }, index)); }, [index, localState, layer, setLocalState] ); + const setPalette = useCallback( + (palette: PaletteOutput) => { + setLocalState(updateLayer(localState, { ...layer, palette }, index)); + }, + [index, localState, layer, setLocalState] + ); const overwriteColor = getSeriesColor(layer, accessor); const assignedColor = useMemo(() => { @@ -153,9 +163,8 @@ export function DataDimensionEditor( const { splitAccessor } = layer; const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); - const canUseColorMapping = layer.colorMapping ? true : false; if (props.groupId === 'breakdown' && !layer.collapseFn) { - return canUseColorMapping ? ( + return ( setIsPaletteOpen(!isPaletteOpen)} title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { - defaultMessage: 'Color assignments', + defaultMessage: 'Color', })} - isTechPreview={true} >
- setColorMapping(model)} - palettes={availablePalettes} - data={{ - type: 'categories', - categories: splitCategories, - specialHandling: SPECIAL_RULE_MATCHES, - }} - /> + + + { + setColorMapping( + e.target.checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + ); + setUseNewColorMapping(e.target.checked); + }} + /> + + + {canUseColorMapping || useNewColorMapping ? ( + setColorMapping(model)} + palettes={availablePalettes} + data={{ + type: 'categories', + categories: splitCategories, + specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, + }} + /> + ) : ( + { + setPalette(newPalette); + }} + /> + )} + +
- ) : ( - { - props.setState(updateLayer(localState, { ...localLayer, palette: newPalette }, index)); - }} - /> ); } From 89d336b894ba4a5cf63d44d315f9540136dba4eb Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 31 Aug 2023 13:30:09 +0200 Subject: [PATCH 13/71] feat: move available palette to coloring package --- .../config/default_color_mapping.ts | 5 ++-- .../shared_components/color_mapping/index.ts | 1 + .../palettes/available_palettes.ts | 24 +++++++++++++++++++ .../palettes/default_palettes.ts | 8 ------- .../partition/dimension_editor.tsx | 16 ++----------- .../tagcloud/tags_dimension_editor.tsx | 17 +++---------- .../xy/xy_config_panel/dimension_editor.tsx | 17 ++----------- 7 files changed, 35 insertions(+), 53 deletions(-) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index 375d93fcf1789..985337716ea43 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -7,7 +7,8 @@ */ import { ColorMapping } from '.'; -import { NeutralPalette, availablePalettes, getPalette } from '../palettes/default_palettes'; +import { AVAILABLE_PALETTES } from '../palettes/available_palettes'; +import { NeutralPalette, getPalette } from '../palettes/default_palettes'; export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { assignmentMode: 'auto', @@ -36,6 +37,6 @@ export function getPaletteColors( colorMappings?: ColorMapping.Config ): string[] { const colorMappingModel = colorMappings ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }; - const palette = getPalette(availablePalettes, NeutralPalette)(colorMappingModel.paletteId); + const palette = getPalette(AVAILABLE_PALETTES, NeutralPalette)(colorMappingModel.paletteId); return Array.from({ length: palette.colorCount }, (d, i) => palette.getColor(i, isDarkMode)); } diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts index 4b16cfadf891d..bf996cbc80bae 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts @@ -9,6 +9,7 @@ export { CategoricalColorMapping } from './categorical_color_mapping'; export type { ColorMappingInputData } from './categorical_color_mapping'; export type { ColorMapping } from './config'; +export * from './palettes/available_palettes'; export * from './palettes/default_palettes'; export * from './color/color_handling'; export { SPECIAL_TOKENS_STRING_CONVERTION } from './color/rule_matching'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts new file mode 100644 index 0000000000000..2a1d9057fc2be --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from '../config'; +import { + EUIPalette, + IKEAPalette, + NeutralPalette, + PastelPalette, + TableauPalette, +} from './default_palettes'; + +export const AVAILABLE_PALETTES = new Map([ + [EUIPalette.id, EUIPalette], + [TableauPalette.id, TableauPalette], + [IKEAPalette.id, IKEAPalette], + [PastelPalette.id, PastelPalette], + [NeutralPalette.id, NeutralPalette], +]); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts index 74880d3f04b69..a2ef7e874deeb 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts @@ -70,14 +70,6 @@ export const NeutralPalette: ColorMapping.CategoricalPalette = { export const DEFAULT_NEUTRAL_PALETTE_INDEX = 2; -export const availablePalettes = new Map([ - [EUIPalette.id, EUIPalette], - [TableauPalette.id, TableauPalette], - [IKEAPalette.id, IKEAPalette], - [PastelPalette.id, PastelPalette], - [NeutralPalette.id, NeutralPalette], -]); - export function getPalette( palettes: Map, defaultPalette: ColorMapping.CategoricalPalette diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index 156a65463eb86..57e6aeb30f739 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -14,12 +14,8 @@ import { PaletteRegistry, getPaletteColors, ColorMapping, - EUIPalette, - IKEAPalette, - NeutralPalette, - PastelPalette, - TableauPalette, SPECIAL_TOKENS_STRING_CONVERTION, + AVAILABLE_PALETTES, } from '@kbn/coloring'; import { ColorPicker, useDebouncedValue } from '@kbn/visualization-ui-components'; import { @@ -127,14 +123,6 @@ export function DimensionEditor(props: DimensionEditorProps) { }) : undefined; - // TODO: move the available palette elsewhere - const availablePalettes = new Map([ - [EUIPalette.id, EUIPalette], - [TableauPalette.id, TableauPalette], - [IKEAPalette.id, IKEAPalette], - [PastelPalette.id, PastelPalette], - [NeutralPalette.id, NeutralPalette], - ]); const colors = getPaletteColors(false, currentLayer.colorMapping); const table = props.frame.activeData?.[currentLayer.layerId]; const splitCategories = getColorCategories(table?.rows ?? [], props.accessor); @@ -204,7 +192,7 @@ export function DimensionEditor(props: DimensionEditorProps) { isDarkMode={props.isDarkMode} model={currentLayer.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }} onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)} - palettes={availablePalettes} + palettes={AVAILABLE_PALETTES} data={{ type: 'categories', categories: splitCategories, diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index b9e485227a471..aa100f6bb4f4e 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -12,13 +12,9 @@ import { DEFAULT_COLOR_MAPPING_CONFIG, ColorMapping, getPaletteColors, - EUIPalette, - IKEAPalette, - NeutralPalette, - PastelPalette, - TableauPalette, SPECIAL_TOKENS_STRING_CONVERTION, PaletteOutput, + AVAILABLE_PALETTES, } from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { @@ -60,14 +56,7 @@ export function TagsDimensionEditor({ }); const [isPaletteOpen, setIsPaletteOpen] = useState(false); const [useNewColorMapping, setUseNewColorMapping] = useState(state.colorMapping ? true : false); - // TODO: move the available palette elsewhere - const availablePalettes = new Map([ - [EUIPalette.id, EUIPalette], - [TableauPalette.id, TableauPalette], - [IKEAPalette.id, IKEAPalette], - [PastelPalette.id, PastelPalette], - [NeutralPalette.id, NeutralPalette], - ]); + const colors = getPaletteColors(false, state.colorMapping); const table = frame.activeData?.[state.layerId]; const splitCategories = getColorCategories(table?.rows ?? [], state.tagAccessor); @@ -158,7 +147,7 @@ export function TagsDimensionEditor({ isDarkMode={isDarkMode} model={state.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }} onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)} - palettes={availablePalettes} + palettes={AVAILABLE_PALETTES} data={{ type: 'categories', categories: splitCategories, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 1b2d6ba8f74cf..cfbc7463af500 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -23,16 +23,12 @@ import { import { PaletteRegistry, ColorMapping, - EUIPalette, - IKEAPalette, - NeutralPalette, - PastelPalette, - TableauPalette, DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors, CategoricalColorMapping, PaletteOutput, SPECIAL_TOKENS_STRING_CONVERTION, + AVAILABLE_PALETTES, } from '@kbn/coloring'; import { getColorCategories } from '@kbn/expression-xy-plugin/public'; @@ -147,15 +143,6 @@ export function DataDimensionEditor( ).color; }, [props.frame, props.paletteService, state.layers, accessor, props.formatFactory, layer]); - // TODO: move the available palette elsewhere - const availablePalettes = new Map([ - [EUIPalette.id, EUIPalette], - [TableauPalette.id, TableauPalette], - [IKEAPalette.id, IKEAPalette], - [PastelPalette.id, PastelPalette], - [NeutralPalette.id, NeutralPalette], - ]); - const localLayer: XYDataLayerConfig = layer; const colors = getPaletteColors(props.darkMode, layer.colorMapping); @@ -227,7 +214,7 @@ export function DataDimensionEditor( isDarkMode={darkMode} model={layer.colorMapping ?? { ...DEFAULT_COLOR_MAPPING_CONFIG }} onModelUpdate={(model: ColorMapping.Config) => setColorMapping(model)} - palettes={availablePalettes} + palettes={AVAILABLE_PALETTES} data={{ type: 'categories', categories: splitCategories, From 79ec6032e68c7c8e170da40b67172736e3f9956d Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 31 Aug 2023 16:04:39 +0200 Subject: [PATCH 14/71] fix moved function --- src/plugins/chart_expressions/common/tsconfig.json | 2 ++ .../chart_expressions/expression_legacy_metric/kibana.jsonc | 3 ++- src/plugins/chart_expressions/expression_metric/kibana.jsonc | 3 ++- .../public/utils/layers/get_layers.ts | 4 ++-- .../public/components/tagcloud_component.tsx | 1 - .../expression_xy/public/helpers/data_layers.tsx | 4 ++-- src/plugins/chart_expressions/expression_xy/public/index.ts | 2 -- .../lens/public/visualizations/partition/dimension_editor.tsx | 2 +- .../public/visualizations/tagcloud/tags_dimension_editor.tsx | 2 +- .../visualizations/xy/xy_config_panel/dimension_editor.tsx | 3 +-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/plugins/chart_expressions/common/tsconfig.json b/src/plugins/chart_expressions/common/tsconfig.json index f65660474561b..7ac76523fcb6c 100644 --- a/src/plugins/chart_expressions/common/tsconfig.json +++ b/src/plugins/chart_expressions/common/tsconfig.json @@ -15,5 +15,7 @@ ], "kbn_references": [ "@kbn/core-execution-context-common", + "@kbn/expressions-plugin", + "@kbn/data-plugin", ] } diff --git a/src/plugins/chart_expressions/expression_legacy_metric/kibana.jsonc b/src/plugins/chart_expressions/expression_legacy_metric/kibana.jsonc index 41a9c965a66da..a49ca80a2fcd2 100644 --- a/src/plugins/chart_expressions/expression_legacy_metric/kibana.jsonc +++ b/src/plugins/chart_expressions/expression_legacy_metric/kibana.jsonc @@ -12,7 +12,8 @@ "fieldFormats", "charts", "visualizations", - "presentationUtil" + "presentationUtil", + "data" ], "optionalPlugins": [ "usageCollection" diff --git a/src/plugins/chart_expressions/expression_metric/kibana.jsonc b/src/plugins/chart_expressions/expression_metric/kibana.jsonc index 087583e6fff6f..a53def7de36ee 100644 --- a/src/plugins/chart_expressions/expression_metric/kibana.jsonc +++ b/src/plugins/chart_expressions/expression_metric/kibana.jsonc @@ -12,7 +12,8 @@ "fieldFormats", "charts", "visualizations", - "presentationUtil" + "presentationUtil", + "data" ], "optionalPlugins": [ "usageCollection" diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index 677018b023656..c65d1dc76949f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -12,8 +12,8 @@ import { getColorFactory, SPECIAL_TOKENS_STRING_CONVERTION, getPalette, - availablePalettes, NeutralPalette, + AVAILABLE_PALETTES, } from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; @@ -147,7 +147,7 @@ function getColorFromMappingFactory( : getColorCategories(rows, columns[0]?.id); return getColorFactory( JSON.parse(colorMapping), - getPalette(availablePalettes, NeutralPalette), + getPalette(AVAILABLE_PALETTES, NeutralPalette), isDarkMode, { type: 'categories', diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index adecb353a70cf..6c39875b6c86f 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -122,7 +122,6 @@ export const TagCloudChart = ({ isDarkMode, colorMapping ); - console.log(`has color mappaing`, colorMapping, colorFromMappingFn); return visData.rows.map((row) => { const tag = tagColumn === undefined ? 'all' : row[tagColumn]; diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 466c49b10efeb..59cb36c283467 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -24,7 +24,7 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; -import { getPalette, availablePalettes, NeutralPalette } from '@kbn/coloring'; +import { getPalette, AVAILABLE_PALETTES, NeutralPalette } from '@kbn/coloring'; import { SPECIAL_TOKENS_STRING_CONVERTION } from '@kbn/coloring/src/shared_components/color_mapping/color/rule_matching'; import { getColorCategories } from '@kbn/chart-expressions-common'; import { isDataLayer } from '../../common/utils/layer_types_guards'; @@ -490,7 +490,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({ layer.colorMapping && splitColumnIds.length > 0 ? getColorSeriesAccessorFn( JSON.parse(layer.colorMapping), // the color mapping is at this point just a strinfigied JSON - getPalette(availablePalettes, NeutralPalette), + getPalette(AVAILABLE_PALETTES, NeutralPalette), isDarkMode, { type: 'categories', diff --git a/src/plugins/chart_expressions/expression_xy/public/index.ts b/src/plugins/chart_expressions/expression_xy/public/index.ts index f2b03a9274c07..7c4e39927d127 100755 --- a/src/plugins/chart_expressions/expression_xy/public/index.ts +++ b/src/plugins/chart_expressions/expression_xy/public/index.ts @@ -17,5 +17,3 @@ export function plugin() { export { LayerTypes, XYCurveTypes } from '../common'; export type { ExpressionXyPluginSetup, ExpressionXyPluginStart } from './types'; - -export { getColorCategories } from './helpers/color/categories'; diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index 57e6aeb30f739..bb2299c429b4c 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -26,7 +26,7 @@ import { EuiSwitch, } from '@elastic/eui'; import { useState, useCallback } from 'react'; -import { getColorCategories } from '@kbn/expression-xy-plugin/public'; +import { getColorCategories } from '@kbn/chart-expressions-common'; import { PieVisualizationState } from '../../../common/types'; import { VisualizationDimensionEditorProps } from '../../types'; import { PalettePanelContainer, PalettePicker } from '../../shared_components'; diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index aa100f6bb4f4e..d1bde38143f83 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -24,10 +24,10 @@ import { EuiFlexItem, EuiSwitch, } from '@elastic/eui'; -import { getColorCategories } from '@kbn/expression-xy-plugin/public'; import { useState, MutableRefObject, useCallback } from 'react'; import { PalettePicker } from '@kbn/coloring/src/shared_components/coloring/palette_picker'; import { useDebouncedValue } from '@kbn/visualization-ui-components'; +import { getColorCategories } from '@kbn/chart-expressions-common'; import type { TagcloudState } from './types'; import { PalettePanelContainer } from '../../shared_components'; import { FramePublicAPI } from '../../types'; diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index cfbc7463af500..348ad356e335a 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -30,8 +30,7 @@ import { SPECIAL_TOKENS_STRING_CONVERTION, AVAILABLE_PALETTES, } from '@kbn/coloring'; - -import { getColorCategories } from '@kbn/expression-xy-plugin/public'; +import { getColorCategories } from '@kbn/chart-expressions-common'; import type { VisualizationDimensionEditorProps } from '../../../types'; import { State, XYState, XYDataLayerConfig, YConfig, YAxisMode } from '../types'; import { FormatFactory } from '../../../../common/types'; From e5303f3cf8b8e517c0145672d04760caf3677943 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 31 Aug 2023 18:08:14 +0200 Subject: [PATCH 15/71] fix: flat opaque colors and theme aware greys colors --- .../color_mapping/color/color_handling.ts | 10 +++-- .../color_mapping/color/color_math.ts | 30 ++++++++++++- .../components/color_picker/color_picker.tsx | 23 +++++----- .../color_picker/palette_colors.tsx | 2 +- .../components/container/container.tsx | 2 +- .../components/palette_selector/gradient.scss | 42 +++++++++---------- .../components/palette_selector/gradient.tsx | 18 ++++---- .../palette_selector/palette_selector.tsx | 2 +- .../palettes/default_palettes.ts | 2 +- .../color_mapping/palettes/neutral.ts | 6 +-- 10 files changed, 85 insertions(+), 52 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index 7e4fd8299f73b..5f29bacd25f2e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -9,7 +9,7 @@ import { scaleSequential } from 'd3-scale'; import { interpolateLab, piecewise } from 'd3-interpolate'; import { ColorMapping } from '../config'; -import { changeAlpha } from './color_math'; +import { changeAlpha, combineColors } from './color_math'; import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; import { getPalette } from '../palette'; import { ColorMappingInputData } from '../categorical_color_mapping'; @@ -36,9 +36,11 @@ export function getAssignmentColor( const steps = colorMode.steps.length === 1 ? [ - // TODO: change lumisonity instead of alpha - changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 1), - changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + getColor(colorMode.steps[0], getPaletteFn, isDarkMode), + combineColors( + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + isDarkMode ? 'black' : 'white' + ), ] : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts index 080bf09086f6b..5a42b3c222d9b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts @@ -7,7 +7,7 @@ */ import chroma from 'chroma-js'; -import { APCAContrast } from './apca'; +import { APCAContrast, RgbaTuple } from './apca'; export function getValidColor(color: string): chroma.Color { try { @@ -26,7 +26,7 @@ export function getColorContrast(color: string, isDark: boolean) { export function changeAlpha(color: string, alpha: number) { const [r, g, b] = getValidColor(color).rgb(); - return `rgba(${r},${g},${b}, ${alpha})`; + return `rgba(${r},${g},${b},${alpha})`; } export function toHex(color: string) { @@ -36,3 +36,29 @@ export function toHex(color: string) { export function isSameColor(color1: string, color2: string) { return toHex(color1) === toHex(color2); } + +/** + * Blend a foreground (fg) color with a background (bg) color + */ +export function combineColors(fg: string, bg: string): string { + const [fgR, fgG, fgB, fgA] = getValidColor(fg).rgba(); + const [bgR, bgG, bgB, bgA] = getValidColor(bg).rgba(); + + // combine colors only if foreground has transparency + if (fgA === 1) { + return chroma.rgb(fgR, fgG, fgB).hex(); + } + + // For reference on alpha calculations: + // https://en.wikipedia.org/wiki/Alpha_compositing + const alpha = fgA + bgA * (1 - fgA); + + if (alpha === 0) { + return '#00000000'; + } + + const r = Math.round((fgR * fgA + bgR * bgA * (1 - fgA)) / alpha); + const g = Math.round((fgG * fgA + bgG * bgA * (1 - fgA)) / alpha); + const b = Math.round((fgB * fgA + bgB * bgA * (1 - fgA)) / alpha); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx index 97464c849e6e1..88ab02a9f686e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx @@ -68,16 +68,19 @@ export function ColorPicker({ /> )} {deleteStep ? ( - { - close(); - deleteStep(); - }} - aria-label="Delete color" - > - Remove from gradient - + <> + + { + close(); + deleteStep(); + }} + aria-label="Delete color" + > + Remove from gradient + + ) : null}
); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx index d5c61f4ed4613..b3f1874b6e062 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx @@ -68,7 +68,7 @@ export function PaletteColors({ - Neutral + Greys (theme aware) (colorMode.sort === 'asc' ? 1 : -1)) + getColor(colorMode.steps[0], getPaletteFn, isDarkMode), + combineColors( + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + isDarkMode ? 'black' : 'white' + ), + ].sort(() => (colorMode.sort === 'asc' ? -1 : 1)) : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)) : []; - const gradientColorScale = scaleSequential(piecewise(interpolateHsl, gradientColorSteps)); + const gradientColorScale = scaleSequential(piecewise(interpolateLab, gradientColorSteps)); const gradientCSSBackground = colorMode.type === 'gradient' ? colorMode.steps.length === 1 @@ -178,9 +181,8 @@ function AddStop({ = 3} - style={{ opacity: 0.3, width: 16, height: 16, paddingLeft: 12 }} + style={{ color: '#696F7D', width: 16, height: 16, paddingLeft: 12 }} className="colorMappingGradientAddStop" - color="text" size="s" onClick={() => { dispatch( diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index 37d42e9efeddd..2e4061f951a2e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -184,7 +184,7 @@ export function PaletteSelector({ }, { id: `gradient`, - label: 'Gradient', + label: 'Sequential', }, ]} isFullWidth diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts index a2ef7e874deeb..cd9a7b1dc9820 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts @@ -68,7 +68,7 @@ export const NeutralPalette: ColorMapping.CategoricalPalette = { }, }; -export const DEFAULT_NEUTRAL_PALETTE_INDEX = 2; +export const DEFAULT_NEUTRAL_PALETTE_INDEX = 1; export function getPalette( palettes: Map, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts index 4f627dea75e66..285a25aa75944 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts @@ -6,6 +6,6 @@ * Side Public License, v 1. */ -const schemeGreys = ['#f7f7f7', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525']; -export const NEUTRAL_COLOR_LIGHT = schemeGreys.slice(1, 6); -export const NEUTRAL_COLOR_DARK = schemeGreys.slice(1, 6); +const schemeGreys = ['#F2F4FB', '#D4D9E5', '#98A2B3', '#696F7D', '#353642']; +export const NEUTRAL_COLOR_LIGHT = schemeGreys.slice(); +export const NEUTRAL_COLOR_DARK = schemeGreys.slice().reverse(); From 41d19b00a4974ca8a231e18b86b1835d8bbc39a7 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 1 Sep 2023 14:48:15 +0200 Subject: [PATCH 16/71] WIP --- .../categorical_color_mapping.test.tsx | 45 +++++++++++++++++++ .../components/container/container.tsx | 7 ++- .../palette_selector/palette_selector.tsx | 1 + 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx new file mode 100644 index 0000000000000..06413c050741b --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { mount } from 'enzyme'; +import { CategoricalColorMapping } from './categorical_color_mapping'; +import { AVAILABLE_PALETTES } from './palettes/available_palettes'; +import { DEFAULT_COLOR_MAPPING_CONFIG } from './config/default_color_mapping'; + +describe('color mapping', () => { + it('load a specified color mapping', () => { + const component = mount( + console.log(m)} + /> + ); + + expect( + component + .find('EuiSwitch[data-test-subj="lns-colorMapping-autoAssignSwitch"]') + .prop('checked') + ).toEqual(true); + + expect( + component + .find('EuiSwitch[data-test-subj="lns-colorMapping-autoAssignSwitch"]') + .prop('checked') + ).toEqual(true); + + component.find('[data-test-subj="lns-colorMapping-assignmentsList"]'). + }); +}); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index e1371c81aa1cf..5af969953a516 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -85,6 +85,7 @@ export function Container(props: { )} - + {assignments.map((assignment, i) => { return ( - + {props.data.type === 'categories' && ( <> @@ -160,6 +161,7 @@ export function Container(props: { { @@ -183,6 +185,7 @@ export function Container(props: { {colorMode.type === 'gradient' && ( { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index 2e4061f951a2e..5b605c0806aa5 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -177,6 +177,7 @@ export function PaletteSelector({ Date: Mon, 4 Sep 2023 11:36:01 +0200 Subject: [PATCH 17/71] add tests --- .../categorical_color_mapping.test.tsx | 109 ++++++-- .../categorical_color_mapping.tsx | 13 +- .../color/color_handling.test.ts | 259 ++++++++++++++++++ .../color_mapping/color/color_math.ts | 4 +- .../components/assignment/assignment.tsx | 4 +- .../components/assignment/match.tsx | 4 +- .../components/container/container.tsx | 3 + .../config/default_color_mapping.ts | 8 +- .../color_mapping/palettes/eui.ts | 40 +-- .../color_mapping/palettes/neutral.ts | 2 +- .../public/utils/layers/get_layers.ts | 2 - .../public/components/tagcloud_component.tsx | 6 +- .../helpers/color/color_mapping_accessor.ts | 10 +- .../public/helpers/data_layers.tsx | 12 +- .../partition/dimension_editor.tsx | 2 +- .../tagcloud/tags_dimension_editor.tsx | 2 +- .../xy/xy_config_panel/dimension_editor.tsx | 2 +- 17 files changed, 411 insertions(+), 71 deletions(-) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx index 06413c050741b..a117ebe1846cc 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx @@ -8,38 +8,105 @@ import React from 'react'; import { mount } from 'enzyme'; -import { CategoricalColorMapping } from './categorical_color_mapping'; +import { CategoricalColorMapping, ColorMappingInputData } from './categorical_color_mapping'; import { AVAILABLE_PALETTES } from './palettes/available_palettes'; import { DEFAULT_COLOR_MAPPING_CONFIG } from './config/default_color_mapping'; +import { MULTI_FIELD_VALUES_SEPARATOR } from './components/assignment/match'; + +const AUTO_ASSIGN_SWITCH = '[data-test-subj="lns-colorMapping-autoAssignSwitch"]'; +const ASSIGNMENTS_LIST = '[data-test-subj="lns-colorMapping-assignmentsList"]'; +const ASSIGNMENT_ITEM = (i: number) => `[data-test-subj="lns-colorMapping-assignmentsItem${i}"]`; describe('color mapping', () => { - it('load a specified color mapping', () => { + it('load a default color mapping', () => { + const dataInput: ColorMappingInputData = { + type: 'categories', + categories: ['categoryA', 'categoryB'], + }; + const onModelUpdateFn = jest.fn(); + const component = mount( + + ); + + expect(component.find(AUTO_ASSIGN_SWITCH).hostNodes().prop('aria-checked')).toEqual(true); + expect(component.find(ASSIGNMENTS_LIST).hostNodes().children().length).toEqual( + dataInput.categories.length + ); + dataInput.categories.forEach((category, index) => { + const assignment = component.find(ASSIGNMENT_ITEM(index)).hostNodes(); + expect(assignment.text()).toEqual(category); + expect(assignment.hasClass('euiComboBox-isDisabled')).toEqual(true); + }); + expect(onModelUpdateFn).not.toBeCalled(); + }); + + it('switch to manual assignments', () => { + const dataInput: ColorMappingInputData = { + type: 'categories', + categories: ['categoryA', 'categoryB'], + }; + const onModelUpdateFn = jest.fn(); const component = mount( console.log(m)} + onModelUpdate={onModelUpdateFn} + specialTokens={new Map()} /> ); + component.find(AUTO_ASSIGN_SWITCH).hostNodes().simulate('click'); + expect(onModelUpdateFn).toBeCalledTimes(1); + expect(component.find(AUTO_ASSIGN_SWITCH).hostNodes().prop('aria-checked')).toEqual(false); + expect(component.find(ASSIGNMENTS_LIST).hostNodes().children().length).toEqual( + dataInput.categories.length + ); + dataInput.categories.forEach((category, index) => { + const assignment = component.find(ASSIGNMENT_ITEM(index)).hostNodes(); + expect(assignment.text()).toEqual(category); + expect(assignment.hasClass('euiComboBox-isDisabled')).toEqual(false); + }); + }); - expect( - component - .find('EuiSwitch[data-test-subj="lns-colorMapping-autoAssignSwitch"]') - .prop('checked') - ).toEqual(true); + it('handle special tokens and multi-fields keys', () => { + const dataInput: ColorMappingInputData = { + type: 'categories', + categories: ['__other__', ['fieldA', 'fieldB'], '__empty__'], + }; + const onModelUpdateFn = jest.fn(); + const component = mount( + + ); + expect(component.find(ASSIGNMENTS_LIST).hostNodes().children().length).toEqual( + dataInput.categories.length + ); + const assignment1 = component.find(ASSIGNMENT_ITEM(0)).hostNodes(); + expect(assignment1.text()).toEqual('Other'); - expect( - component - .find('EuiSwitch[data-test-subj="lns-colorMapping-autoAssignSwitch"]') - .prop('checked') - ).toEqual(true); + const assignment2 = component.find(ASSIGNMENT_ITEM(1)).hostNodes(); + expect(assignment2.text()).toEqual(`fieldA${MULTI_FIELD_VALUES_SEPARATOR}fieldB`); - component.find('[data-test-subj="lns-colorMapping-assignmentsList"]'). + const assignment3 = component.find(ASSIGNMENT_ITEM(2)).hostNodes(); + expect(assignment3.text()).toEqual('(Empty)'); }); }); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx index b3ea219204d33..ca99e08d248ae 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx @@ -20,7 +20,6 @@ export type ColorMappingInputData = type: 'categories'; /** an ORDERED array of categories rendered in the visualization */ categories: Array; - specialTokens: Map; } | { type: 'ranges'; @@ -36,7 +35,8 @@ export interface ColorMappingProps { palettes: Map; data: ColorMappingInputData; isDarkMode: boolean; - + /** map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ + specialTokens: Map; /** a function called at every change in the model */ onModelUpdate: (model: ColorMapping.Config) => void; } @@ -73,10 +73,15 @@ export class CategoricalColorMapping extends React.Component } } render() { - const { palettes, data, isDarkMode } = this.props; + const { palettes, data, isDarkMode, specialTokens } = this.props; return ( - + ); } diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts new file mode 100644 index 0000000000000..3bdcf0118c035 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts @@ -0,0 +1,259 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { DEFAULT_COLOR_MAPPING_CONFIG } from '../config/default_color_mapping'; +import { getColorFactory } from './color_handling'; +import { getPalette } from '../palette'; +import { AVAILABLE_PALETTES } from '../palettes/available_palettes'; +import { + DEFAULT_NEUTRAL_PALETTE_INDEX, + EUIPalette, + NeutralPalette, +} from '../palettes/default_palettes'; +import { EUI_PALETTE_COLORS_DARK, EUI_PALETTE_COLORS_LIGHT } from '../palettes/eui'; +import { NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from '../palettes/neutral'; +import { toHex } from './color_math'; + +import { ColorMapping } from '../config'; + +describe('Color mapping - color generation', () => { + const getPaletteFn = getPalette(AVAILABLE_PALETTES, NeutralPalette); + it('returns EUI light colors from default config', () => { + const colorFactory = getColorFactory(DEFAULT_COLOR_MAPPING_CONFIG, getPaletteFn, false, { + type: 'categories', + categories: ['catA', 'catB', 'catC'], + }); + expect(colorFactory('catA')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); + expect(colorFactory('catB')).toBe(EUI_PALETTE_COLORS_LIGHT[1]); + expect(colorFactory('catC')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); + // if the category is not available in the `categories` list then a default neutral color is used + expect(colorFactory('not_available')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]); + }); + + it('returns max number of colors defined in palette, use other color otherwise', () => { + const twoColorPalette: ColorMapping.CategoricalPalette = { + id: 'twoColors', + name: 'twoColors', + colorCount: 2, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return ['red', 'blue'][valueInRange]; + }, + }; + + const simplifiedGetPaletteGn = getPalette( + new Map([[twoColorPalette.id, twoColorPalette]]), + NeutralPalette + ); + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + paletteId: twoColorPalette.id, + }, + simplifiedGetPaletteGn, + false, + { + type: 'categories', + categories: ['cat1', 'cat2', 'cat3', 'cat4'], + } + ); + expect(colorFactory('cat1')).toBe('red'); + expect(colorFactory('cat2')).toBe('blue'); + // return a palette color only up to the max number of color in the palette + expect(colorFactory('cat3')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]); + expect(colorFactory('cat4')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]); + }); + + // currently there is no difference in the two colors, but this could change in the future + // this test will catch the change + it('returns EUI dark colors from default config', () => { + const colorFactory = getColorFactory(DEFAULT_COLOR_MAPPING_CONFIG, getPaletteFn, true, { + type: 'categories', + categories: ['catA', 'catB', 'catC'], + }); + expect(colorFactory('catA')).toBe(EUI_PALETTE_COLORS_DARK[0]); + expect(colorFactory('catB')).toBe(EUI_PALETTE_COLORS_DARK[1]); + expect(colorFactory('catC')).toBe(EUI_PALETTE_COLORS_DARK[2]); + // if the category is not available in the `categories` list then a default neutral color is used + expect(colorFactory('not_available')).toBe(NEUTRAL_COLOR_DARK[DEFAULT_NEUTRAL_PALETTE_INDEX]); + }); + + it('handles special tokens and multi-field categories', () => { + const colorFactory = getColorFactory(DEFAULT_COLOR_MAPPING_CONFIG, getPaletteFn, false, { + type: 'categories', + categories: ['__other__', ['fieldA', 'fieldB'], '__empty__'], + }); + expect(colorFactory('__other__')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); + expect(colorFactory(['fieldA', 'fieldB'])).toBe(EUI_PALETTE_COLORS_LIGHT[1]); + expect(colorFactory('__empty__')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); + }); + + it('ignores configured assignments in auto mode', () => { + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + assignments: [ + { + color: { type: 'colorCode', colorCode: 'red' }, + rule: { type: 'matchExactly', values: ['assignmentToIgnore'] }, + touched: false, + }, + ], + }, + getPaletteFn, + false, + { + type: 'categories', + categories: ['catA', 'catB', 'assignmentToIgnore'], + } + ); + expect(colorFactory('catA')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); + expect(colorFactory('catB')).toBe(EUI_PALETTE_COLORS_LIGHT[1]); + expect(colorFactory('assignmentToIgnore')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); + }); + + it('color with auto rule are assigned in order of the configured data input', () => { + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + assignmentMode: 'manual', + assignments: [ + { + color: { type: 'colorCode', colorCode: 'red' }, + rule: { type: 'auto' }, + touched: false, + }, + { + color: { type: 'colorCode', colorCode: 'blue' }, + rule: { type: 'matchExactly', values: ['blueCat'] }, + touched: false, + }, + { + color: { type: 'colorCode', colorCode: 'green' }, + rule: { type: 'auto' }, + touched: false, + }, + ], + }, + getPaletteFn, + false, + { + type: 'categories', + categories: ['blueCat', 'redCat', 'greenCat'], + } + ); + // this matches exactly + expect(colorFactory('blueCat')).toBe('blue'); + // this matches with the first availabe "auto" rule + expect(colorFactory('redCat')).toBe('red'); + // this matches with the second availabe "auto" rule + expect(colorFactory('greenCat')).toBe('green'); + // if the category is not available in the `categories` list then a default neutral color is used + expect(colorFactory('not_available')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]); + }); + + it('returns sequential gradient colors from darker to lighter [desc, lightMode]', () => { + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + colorMode: { + type: 'gradient', + steps: [{ type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }], + sort: 'desc', + }, + }, + getPaletteFn, + false, + { + type: 'categories', + categories: ['cat1', 'cat2', 'cat3'], + } + ); + // this matches exactly with the initial step selected + expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); + expect(toHex(colorFactory('cat2'))).toBe('#93cebc'); + expect(toHex(colorFactory('cat3'))).toBe('#cce8e0'); + }); + + it('returns sequential gradient colors from lighter to darker [asc, lightMode]', () => { + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + colorMode: { + type: 'gradient', + steps: [{ type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }], + sort: 'asc', + }, + }, + getPaletteFn, + false, + { + type: 'categories', + categories: ['cat1', 'cat2', 'cat3'], + } + ); + expect(toHex(colorFactory('cat1'))).toBe('#cce8e0'); + expect(toHex(colorFactory('cat2'))).toBe('#93cebc'); + // this matches exactly with the initial step selected + expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); + }); + + it('returns 2 colors gradient [desc, lightMode]', () => { + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + colorMode: { + type: 'gradient', + steps: [ + { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }, + { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 2, touched: false }, + ], + sort: 'desc', + }, + }, + getPaletteFn, + false, + { + type: 'categories', + categories: ['cat1', 'cat2', 'cat3'], + } + ); + expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); // EUI green + expect(toHex(colorFactory('cat2'))).toBe('#a3908f'); // red gray green + expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[2])); // EUI pink + }); + + it('returns divergent gradient [asc, darkMode]', () => { + const colorFactory = getColorFactory( + { + ...DEFAULT_COLOR_MAPPING_CONFIG, + colorMode: { + type: 'gradient', + steps: [ + { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }, + { type: 'categorical', paletteId: NeutralPalette.id, colorIndex: 0, touched: false }, + { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 2, touched: false }, + ], + sort: 'asc', // testing in ascending order + }, + }, + getPaletteFn, + true, // testing in dark mode + { + type: 'categories', + categories: ['cat1', 'cat2', 'cat3'], + } + ); + expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_DARK[2])); // EUI pink + expect(toHex(colorFactory('cat2'))).toBe(NEUTRAL_COLOR_DARK[0]); // NEUTRAL LIGHT GRAY + expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); // EUI green + expect(toHex(colorFactory('not available cat'))).toBe( + toHex(NEUTRAL_COLOR_DARK[DEFAULT_NEUTRAL_PALETTE_INDEX]) + ); // check the other + }); +}); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts index 5a42b3c222d9b..71415581601f7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts @@ -7,7 +7,7 @@ */ import chroma from 'chroma-js'; -import { APCAContrast, RgbaTuple } from './apca'; +import { APCAContrast } from './apca'; export function getValidColor(color: string): chroma.Color { try { @@ -30,7 +30,7 @@ export function changeAlpha(color: string, alpha: number) { } export function toHex(color: string) { - return getValidColor(color).hex(); + return getValidColor(color).hex().toLowerCase(); } export function isSameColor(color1: string, color2: string) { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index dc78c695212ce..0ea063741b8cd 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -33,6 +33,7 @@ export function Assignment({ colorMode, getPaletteFn, isDarkMode, + specialTokens, }: { data: ColorMappingInputData; index: number; @@ -44,6 +45,7 @@ export function Assignment({ getPaletteFn: ReturnType; canPickColor: boolean; isDarkMode: boolean; + specialTokens: Map; }) { const dispatch = useDispatch(); @@ -75,7 +77,7 @@ export function Assignment({ index={index} rule={assignment.rule} options={data.type === 'categories' ? data.categories : []} - specialTokens={data.type === 'categories' ? data.specialTokens : new Map()} + specialTokens={specialTokens} updateValue={(values: Array) => { dispatch( updateAssignmentRule({ diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index a480913ad1959..37fa220e9a1f6 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -23,7 +23,7 @@ export const Match: React.FC<{ updateValue: (values: Array) => void; options: Array; specialTokens: Map; -}> = ({ rule, updateValue, editable, options, specialTokens }) => { +}> = ({ index, rule, updateValue, editable, options, specialTokens }) => { const selectedOptions = rule.type === 'auto' ? [] @@ -50,6 +50,7 @@ export const Match: React.FC<{ return ( ); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index 5af969953a516..c1d6500d4ae49 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -58,6 +58,8 @@ export function Container(props: { palettes: Map; data: ColorMappingInputData; isDarkMode: boolean; + /** map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ + specialTokens: Map; }) { const dispatch = useDispatch(); @@ -131,6 +133,7 @@ export function Container(props: { getPaletteFn={getPaletteFn} assignment={assignment} disableDelete={assignments.length <= 1 || autoAssignmentMode} + specialTokens={props.specialTokens} /> ); })} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index 985337716ea43..2c05468d0290f 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -8,7 +8,11 @@ import { ColorMapping } from '.'; import { AVAILABLE_PALETTES } from '../palettes/available_palettes'; -import { NeutralPalette, getPalette } from '../palettes/default_palettes'; +import { + NeutralPalette, + getPalette, + DEFAULT_NEUTRAL_PALETTE_INDEX, +} from '../palettes/default_palettes'; export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { assignmentMode: 'auto', @@ -21,7 +25,7 @@ export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { color: { type: 'categorical', paletteId: NeutralPalette.id, - colorIndex: 2, + colorIndex: DEFAULT_NEUTRAL_PALETTE_INDEX, }, touched: false, }, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts index a10bdf3c9ed02..48d76861dbf21 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts @@ -7,26 +7,26 @@ */ export const EUI_PALETTE_COLORS_LIGHT = [ - '#54B399', - '#6092C0', - '#D36086', - '#9170B8', - '#CA8EAE', - '#D6BF57', - '#B9A888', - '#DA8B45', - '#AA6556', - '#E7664C', + '#54b399', + '#6092c0', + '#d36086', + '#9170b8', + '#ca8eae', + '#d6bf57', + '#b9a888', + '#da8b45', + '#aa6556', + '#e7664c', ]; export const EUI_PALETTE_COLORS_DARK = [ - '#54B399', - '#6092C0', - '#D36086', - '#9170B8', - '#CA8EAE', - '#D6BF57', - '#B9A888', - '#DA8B45', - '#AA6556', - '#E7664C', + '#54b399', + '#6092c0', + '#d36086', + '#9170b8', + '#ca8eae', + '#d6bf57', + '#b9a888', + '#da8b45', + '#aa6556', + '#e7664c', ]; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts index 285a25aa75944..eb432518f67b6 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts @@ -6,6 +6,6 @@ * Side Public License, v 1. */ -const schemeGreys = ['#F2F4FB', '#D4D9E5', '#98A2B3', '#696F7D', '#353642']; +const schemeGreys = ['#f2f4fb', '#d4d9e5', '#98a2b3', '#696f7d', '#353642']; export const NEUTRAL_COLOR_LIGHT = schemeGreys.slice(); export const NEUTRAL_COLOR_DARK = schemeGreys.slice().reverse(); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index c65d1dc76949f..c6100b7d64151 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -10,7 +10,6 @@ import { Datum, PartitionLayer } from '@elastic/charts'; import { PaletteRegistry, getColorFactory, - SPECIAL_TOKENS_STRING_CONVERTION, getPalette, NeutralPalette, AVAILABLE_PALETTES, @@ -152,7 +151,6 @@ function getColorFromMappingFactory( { type: 'categories', categories, - specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, } ); } diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 6c39875b6c86f..40a87c3e3aa7d 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -18,9 +18,8 @@ import { PaletteOutput, getColorFactory, getPalette, - availablePalettes, + AVAILABLE_PALETTES, NeutralPalette, - SPECIAL_TOKENS_STRING_CONVERTION, } from '@kbn/coloring'; import { IInterpreterRenderHandlers, DatatableRow } from '@kbn/expressions-plugin/public'; import { getColorCategories, getOverridesFor } from '@kbn/chart-expressions-common'; @@ -320,12 +319,11 @@ function getColorFromMappingFactory( } return getColorFactory( JSON.parse(colorMapping), - getPalette(availablePalettes, NeutralPalette), + getPalette(AVAILABLE_PALETTES, NeutralPalette), isDarkMode, { type: 'categories', categories: getColorCategories(rows, tagColumn), - specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, } ); } diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts index acc6cf817d559..80c5f53bbce02 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts @@ -18,14 +18,14 @@ export function getColorSeriesAccessorFn( getPaletteFn: (paletteId: string) => ColorMapping.CategoricalPalette, isDarkMode: boolean, mappingData: ColorMappingInputData, - fieldId: string + fieldId: string, + specialTokens: Map ): SeriesColorAccessorFn { // inverse map to handle the conversion between the formatted string and their original format // for any specified special tokens - const specialHandlingInverseMap: Map = - mappingData.type === 'categories' - ? new Map([...mappingData.specialTokens.entries()].map((d) => [d[1], d[0]])) - : new Map(); + const specialHandlingInverseMap: Map = new Map( + [...specialTokens.entries()].map((d) => [d[1], d[0]]) + ); const getColor = getColorFactory(config, getPaletteFn, isDarkMode, mappingData); diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx index 59cb36c283467..1971409ab4223 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/data_layers.tsx @@ -24,8 +24,12 @@ import { Datatable } from '@kbn/expressions-plugin/common'; import { getAccessorByDimension } from '@kbn/visualizations-plugin/common/utils'; import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common/expression_functions'; import { PaletteRegistry, SeriesLayer } from '@kbn/coloring'; -import { getPalette, AVAILABLE_PALETTES, NeutralPalette } from '@kbn/coloring'; -import { SPECIAL_TOKENS_STRING_CONVERTION } from '@kbn/coloring/src/shared_components/color_mapping/color/rule_matching'; +import { + getPalette, + AVAILABLE_PALETTES, + NeutralPalette, + SPECIAL_TOKENS_STRING_CONVERTION, +} from '@kbn/coloring'; import { getColorCategories } from '@kbn/chart-expressions-common'; import { isDataLayer } from '../../common/utils/layer_types_guards'; import { CommonXYDataLayerConfig, CommonXYLayerConfig, XScaleType } from '../../common'; @@ -495,9 +499,9 @@ export const getSeriesProps: GetSeriesPropsFn = ({ { type: 'categories', categories: getColorCategories(table.rows, splitColumnIds[0]), - specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, }, - splitColumnIds[0] + splitColumnIds[0], + SPECIAL_TOKENS_STRING_CONVERTION ) : (series) => getColor( diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index bb2299c429b4c..fcb2096f28577 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -196,8 +196,8 @@ export function DimensionEditor(props: DimensionEditorProps) { data={{ type: 'categories', categories: splitCategories, - specialTokens: SPECIAL_TOKENS_STRING_CONVERTION, }} + specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} /> ) : ( ) : ( ) : ( Date: Mon, 4 Sep 2023 14:05:50 +0000 Subject: [PATCH 18/71] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/lens/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 484734f1ae38f..321e8b1fd0161 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -86,6 +86,7 @@ "@kbn/content-management-utils", "@kbn/serverless", "@kbn/ebt-tools", + "@kbn/chart-expressions-common", ], "exclude": [ "target/**/*", From c9c5b0d3935a8a8f347893e35138ba8af3cd70ce Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 6 Sep 2023 16:38:54 +0200 Subject: [PATCH 19/71] update color function tests --- .../palette_selector/palette_selector.tsx | 2 + .../config/default_color_mapping.ts | 3 +- .../mosaic_vis_function.ts | 1 + .../expression_functions/pie_vis_function.ts | 1 + .../treemap_vis_function.ts | 1 + .../waffle_vis_function.ts | 1 + .../common/types/expression_functions.ts | 1 - .../components/tagcloud_component.test.tsx | 2 + .../public/components/tagcloud_component.tsx | 1 + .../tagcloud_renderer.tsx | 2 + .../charts/public/services/theme/theme.ts | 2 +- .../editor_frame/state_helpers.ts | 4 +- .../editor_frame/suggestion_helpers.test.ts | 16 +++++- .../editor_frame/suggestion_helpers.ts | 8 +-- x-pack/plugins/lens/public/types.ts | 19 +++++-- .../partition/dimension_editor.tsx | 4 +- .../visualizations/partition/suggestions.ts | 34 ++++++++++-- .../visualizations/partition/to_expression.ts | 1 + .../partition/visualization.tsx | 55 +++++++++++++++---- .../visualizations/tagcloud/suggestions.ts | 6 ++ .../tagcloud/tags_dimension_editor.tsx | 6 +- .../visualizations/xy/visualization.tsx | 16 +++++- .../xy/xy_config_panel/dimension_editor.tsx | 11 +++- .../visualizations/xy/xy_suggestions.ts | 21 ++++--- .../functional/apps/lens/group4/colors.ts | 2 +- .../test/functional/page_objects/lens_page.ts | 33 ++++++----- 26 files changed, 192 insertions(+), 61 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index 5b605c0806aa5..a70c132daebd9 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -147,9 +147,11 @@ export function PaletteSelector({ d.name !== 'Neutral') .map((palette) => ({ + 'data-test-subj': `kbnColoring_ColorMapping_Palette-${palette.id}`, value: palette.id, title: palette.name, palette: Array.from({ length: palette.colorCount }, (_, i) => { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index 2c05468d0290f..f8a7cd7780a66 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -12,6 +12,7 @@ import { NeutralPalette, getPalette, DEFAULT_NEUTRAL_PALETTE_INDEX, + IKEAPalette, } from '../palettes/default_palettes'; export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { @@ -30,7 +31,7 @@ export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { touched: false, }, ], - paletteId: 'eui', + paletteId: IKEAPalette.id, colorMode: { type: 'categorical', }, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index 89aec849adf0a..4a9dff714c8da 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -150,6 +150,7 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ splitColumn: args.splitColumn, splitRow: args.splitRow, }, + colorMapping: args.colorMapping, }; if (handlers?.inspectorAdapters?.tables) { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index cc64beabf7090..30e8388f1255e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -177,6 +177,7 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ splitColumn: args.splitColumn, splitRow: args.splitRow, }, + colorMapping: args.colorMapping, }; if (handlers?.inspectorAdapters?.tables) { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index c7bc7557b98bd..e0804dd9b0e92 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -156,6 +156,7 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => splitColumn: args.splitColumn, splitRow: args.splitRow, }, + colorMapping: args.colorMapping, }; if (handlers?.inspectorAdapters?.tables) { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index f50f410847e20..6e23513851b1e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -151,6 +151,7 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ splitColumn: args.splitColumn, splitRow: args.splitRow, }, + colorMapping: args.colorMapping, }; if (handlers?.inspectorAdapters?.tables) { diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts index f1c5d40ca430e..c59e70a5c028d 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts @@ -43,7 +43,6 @@ export interface TagcloudRendererConfig { visData: Datatable; visParams: TagCloudRendererParams; syncColors: boolean; - isDarkMode: boolean; overrides?: AllowedSettingsOverrides & AllowedChartOverrides; } diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx index 3f9c86778e82d..86c4bc009d931 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.test.tsx @@ -105,6 +105,7 @@ describe('TagCloudChart', function () { renderComplete: jest.fn(), syncColors: false, visType: 'tagcloud', + isDarkMode: false, }; wrapperPropsWithColumnNames = { @@ -135,6 +136,7 @@ describe('TagCloudChart', function () { renderComplete: jest.fn(), syncColors: false, visType: 'tagcloud', + isDarkMode: false, }; }); diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 40a87c3e3aa7d..719555ced2621 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -40,6 +40,7 @@ export type TagCloudChartProps = TagcloudRendererConfig & { renderComplete: IInterpreterRenderHandlers['done']; palettesRegistry: PaletteRegistry; overrides?: AllowedSettingsOverrides & AllowedChartOverrides; + isDarkMode: boolean; }; const calculateWeight = (value: number, x1: number, y1: number, x2: number, y2: number) => diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx index b3ab496447754..fd39f6c1287d9 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx @@ -67,6 +67,7 @@ export const tagcloudRenderer: ( }; const palettesRegistry = await plugins.charts.palettes.getPalettes(); + const isDarkMode = plugins.charts.theme.useDarkMode(); render( @@ -87,6 +88,7 @@ export const tagcloudRenderer: ( fireEvent={handlers.event} syncColors={config.syncColors} overrides={config.overrides} + isDarkMode={isDarkMode} /> )} diff --git a/src/plugins/charts/public/services/theme/theme.ts b/src/plugins/charts/public/services/theme/theme.ts index dff300a02aa8a..33c2163449ecd 100644 --- a/src/plugins/charts/public/services/theme/theme.ts +++ b/src/plugins/charts/public/services/theme/theme.ts @@ -12,7 +12,7 @@ import { Observable, BehaviorSubject } from 'rxjs'; import { CoreSetup, CoreTheme } from '@kbn/core/public'; import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; - +console.log({ EUI_CHARTS_THEME_LIGHT, EUI_CHARTS_THEME_DARK }); export class ThemeService { /** Returns default charts theme */ public readonly chartsDefaultTheme = EUI_CHARTS_THEME_LIGHT.theme; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 0e677804d5f16..d9cfa8c84c62f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -18,6 +18,7 @@ import { type EventAnnotationGroupConfig, EVENT_ANNOTATION_GROUP_TYPE, } from '@kbn/event-annotation-common'; +import { DEFAULT_COLOR_MAPPING_CONFIG } from '@kbn/coloring'; import type { Datasource, DatasourceMap, @@ -289,7 +290,8 @@ export function initializeVisualization({ visualizationMap[visualizationState.activeId]?.initialize( () => '', visualizationState.state, - undefined, + // initialize a new visualization always with the new color mapping + { type: 'colorMapping', value: { ...DEFAULT_COLOR_MAPPING_CONFIG } }, annotationGroups, references ) ?? visualizationState.state diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts index e86f602465584..235e3b34538b8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts @@ -457,7 +457,13 @@ describe('suggestion helpers', () => { it('should pass passed in main palette if specified', () => { const mockVisualization1 = createMockVisualization(); const mockVisualization2 = createMockVisualization(); - const mainPalette: PaletteOutput = { type: 'palette', name: 'mock' }; + const mainPalette: { type: 'legacyPalette'; value: PaletteOutput } = { + type: 'legacyPalette', + value: { + type: 'palette', + name: 'mock', + }, + }; datasourceMap.mock.getDatasourceSuggestionsFromCurrentState.mockReturnValue([ generateSuggestion(0), generateSuggestion(1), @@ -490,7 +496,13 @@ describe('suggestion helpers', () => { it('should query active visualization for main palette if not specified', () => { const mockVisualization1 = createMockVisualization(); const mockVisualization2 = createMockVisualization(); - const mainPalette: PaletteOutput = { type: 'palette', name: 'mock' }; + const mainPalette: { type: 'legacyPalette'; value: PaletteOutput } = { + type: 'legacyPalette', + value: { + type: 'palette', + name: 'mock', + }, + }; mockVisualization1.getMainPalette = jest.fn(() => mainPalette); datasourceMap.mock.getDatasourceSuggestionsFromCurrentState.mockReturnValue([ generateSuggestion(0), diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index 6679e8b042480..c1032d144ac33 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -6,12 +6,11 @@ */ import type { Datatable } from '@kbn/expressions-plugin/common'; -import type { PaletteOutput } from '@kbn/coloring'; import type { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import type { DragDropIdentifier } from '@kbn/dom-drag-drop'; import { showMemoizedErrorNotification } from '../../lens_ui_errors'; -import type { +import { Visualization, Datasource, TableSuggestion, @@ -21,6 +20,7 @@ import type { VisualizeEditorContext, Suggestion, DatasourceLayers, + SuggestionRequest, } from '../../types'; import type { LayerType } from '../../../common/types'; import { @@ -64,7 +64,7 @@ export function getSuggestions({ visualizeTriggerFieldContext?: VisualizeFieldContext | VisualizeEditorContext; activeData?: Record; dataViews: DataViewsState; - mainPalette?: PaletteOutput; + mainPalette?: SuggestionRequest['mainPalette']; allowMixed?: boolean; }): Suggestion[] { const datasources = Object.entries(datasourceMap).filter( @@ -237,7 +237,7 @@ function getVisualizationSuggestions( datasourceSuggestion: DatasourceSuggestion & { datasourceId: string }, currentVisualizationState: unknown, subVisualizationId?: string, - mainPalette?: PaletteOutput, + mainPalette?: SuggestionRequest['mainPalette'], isFromContext?: boolean, activeData?: Record, allowMixed?: boolean diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 13651c3bc5e69..9b16fe2191a49 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -7,7 +7,7 @@ import type { Ast } from '@kbn/interpreter'; import type { IconType } from '@elastic/eui/src/components/icon/icon'; import type { CoreStart, SavedObjectReference, ResolvedSimpleSavedObject } from '@kbn/core/public'; -import type { PaletteOutput } from '@kbn/coloring'; +import type { ColorMapping, PaletteOutput } from '@kbn/coloring'; import type { TopNavMenuData } from '@kbn/navigation-plugin/public'; import type { MutableRefObject, ReactElement } from 'react'; import type { Filter, TimeRange } from '@kbn/es-query'; @@ -861,7 +861,12 @@ export interface SuggestionRequest { * State is only passed if the visualization is active. */ state?: T; - mainPalette?: PaletteOutput; + /** + * Passing the legacy palette or the new color mapping if available + */ + mainPalette?: + | { type: 'legacyPalette'; value: PaletteOutput } + | { type: 'colorMapping'; value: ColorMapping.Config }; isFromContext?: boolean; /** * The visualization needs to know which table is being suggested @@ -1023,11 +1028,15 @@ export interface Visualization string, nonPersistedState?: T, mainPalette?: PaletteOutput): T; + ( + addNewLayer: () => string, + nonPersistedState?: T, + mainPalette?: SuggestionRequest['mainPalette'] + ): T; ( addNewLayer: () => string, persistedState: P, - mainPalette?: PaletteOutput, + mainPalette?: SuggestionRequest['mainPalette'], annotationGroups?: AnnotationGroups, references?: SavedObjectReference[] ): T; @@ -1039,7 +1048,7 @@ export interface Visualization string[]; - getMainPalette?: (state: T) => undefined | PaletteOutput; + getMainPalette?: (state: T) => undefined | SuggestionRequest['mainPalette']; /** * Supported triggers of this visualization type when embedded somewhere diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index fcb2096f28577..8cd1d5b38cd9e 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -138,7 +138,7 @@ export function DimensionEditor(props: DimensionEditorProps) { > { @@ -148,7 +148,7 @@ export function DimensionEditor(props: DimensionEditorProps) { col.columnId), metrics: metricColumnIds, layerType: layerTypes.DATA, + colorMapping: !mainPalette + ? { ...DEFAULT_COLOR_MAPPING_CONFIG } + : mainPalette?.type === 'colorMapping' + ? mainPalette.value + : state.layers[0].colorMapping, } : { layerId: table.layerId, @@ -150,6 +156,11 @@ export function suggestions({ legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, layerType: layerTypes.DATA, + colorMapping: !mainPalette + ? { ...DEFAULT_COLOR_MAPPING_CONFIG } + : mainPalette?.type === 'colorMapping' + ? mainPalette.value + : undefined, }, ], }, @@ -196,7 +207,7 @@ export function suggestions({ score: state?.shape === PieChartTypes.TREEMAP ? 0.7 : 0.5, state: { shape: PieChartTypes.TREEMAP, - palette: mainPalette || state?.palette, + palette: mainPalette?.type === 'legacyPalette' ? mainPalette.value : state?.palette, layers: [ state?.layers[0] ? { @@ -209,6 +220,10 @@ export function suggestions({ ? CategoryDisplay.DEFAULT : state.layers[0].categoryDisplay, layerType: layerTypes.DATA, + colorMapping: + mainPalette?.type === 'colorMapping' + ? mainPalette.value + : state.layers[0].colorMapping, } : { layerId: table.layerId, @@ -219,6 +234,7 @@ export function suggestions({ legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, layerType: layerTypes.DATA, + colorMapping: mainPalette?.type === 'colorMapping' ? mainPalette.value : undefined, }, ], }, @@ -243,7 +259,7 @@ export function suggestions({ score: state?.shape === PieChartTypes.MOSAIC ? 0.7 : 0.5, state: { shape: PieChartTypes.MOSAIC, - palette: mainPalette || state?.palette, + palette: mainPalette?.type === 'legacyPalette' ? mainPalette.value : state?.palette, layers: [ state?.layers[0] ? { @@ -255,6 +271,10 @@ export function suggestions({ categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, allowMultipleMetrics: false, + colorMapping: + mainPalette?.type === 'colorMapping' + ? mainPalette.value + : state.layers[0].colorMapping, } : { layerId: table.layerId, @@ -267,6 +287,7 @@ export function suggestions({ nestedLegend: false, layerType: layerTypes.DATA, allowMultipleMetrics: false, + colorMapping: mainPalette?.type === 'colorMapping' ? mainPalette.value : undefined, }, ], }, @@ -290,7 +311,7 @@ export function suggestions({ score: state?.shape === PieChartTypes.WAFFLE ? 0.7 : 0.4, state: { shape: PieChartTypes.WAFFLE, - palette: mainPalette || state?.palette, + palette: mainPalette?.type === 'legacyPalette' ? mainPalette.value : state?.palette, layers: [ state?.layers[0] ? { @@ -301,6 +322,10 @@ export function suggestions({ secondaryGroups: [], categoryDisplay: CategoryDisplay.DEFAULT, layerType: layerTypes.DATA, + colorMapping: + mainPalette?.type === 'colorMapping' + ? mainPalette.value + : state.layers[0].colorMapping, } : { layerId: table.layerId, @@ -311,6 +336,7 @@ export function suggestions({ legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, layerType: layerTypes.DATA, + colorMapping: mainPalette?.type === 'colorMapping' ? mainPalette.value : undefined, }, ], }, diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 29e5fd399d148..177c6f154db25 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -175,6 +175,7 @@ const generateCommonArguments = ( const datasource = datasourceLayers[layer.layerId]; const columnToLabelMap = getColumnToLabelMap(layer.metrics, datasource); const sortedMetricAccessors = getSortedAccessorsForGroup(datasource, layer, 'metrics'); + console.log(state); return { labels: generateCommonLabelsAstArgs(state, attributes, layer, columnToLabelMap), buckets: operations diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 2f67177931778..dea43a5847e0f 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -8,7 +8,12 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DEFAULT_COLOR_MAPPING_CONFIG, PaletteRegistry } from '@kbn/coloring'; +import { + ColorMapping, + DEFAULT_COLOR_MAPPING_CONFIG, + getPaletteColors, + PaletteRegistry, +} from '@kbn/coloring'; import { ThemeServiceStart } from '@kbn/core/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; import { EuiSpacer } from '@elastic/eui'; @@ -52,7 +57,7 @@ const metricLabel = i18n.translate('xpack.lens.pie.groupMetricLabelSingular', { defaultMessage: 'Metric', }); -function newLayerState(layerId: string): PieLayerState { +function newLayerState(layerId: string, colorMapping: ColorMapping.Config): PieLayerState { return { layerId, primaryGroups: [], @@ -63,7 +68,7 @@ function newLayerState(layerId: string): PieLayerState { legendDisplay: LegendDisplay.DEFAULT, nestedLegend: false, layerType: LayerTypes.DATA, - colorMapping: { ...DEFAULT_COLOR_MAPPING_CONFIG }, + colorMapping, }; } @@ -139,7 +144,9 @@ export const getPieVisualization = ({ clearLayer(state) { return { shape: state.shape, - layers: state.layers.map((l) => newLayerState(l.layerId)), + layers: state.layers.map((l) => + newLayerState(l.layerId, { ...DEFAULT_COLOR_MAPPING_CONFIG }) + ), }; }, @@ -155,16 +162,33 @@ export const getPieVisualization = ({ triggers: [VIS_EVENT_TO_TRIGGER.filter], initialize(addNewLayer, state, mainPalette) { + console.log('initialize', { state, mainPalette }); return ( state || { shape: PieChartTypes.DONUT, - layers: [newLayerState(addNewLayer())], - palette: mainPalette, + layers: [ + newLayerState( + addNewLayer(), + mainPalette?.type === 'colorMapping' + ? mainPalette.value + : { ...DEFAULT_COLOR_MAPPING_CONFIG } + ), + ], + palette: mainPalette?.type === 'legacyPalette' ? mainPalette.value : undefined, } ); }, - getMainPalette: (state) => (state ? state.palette : undefined), + getMainPalette: (state) => { + if (!state) { + return undefined; + } + return state.layers.length > 0 && state.layers[0].colorMapping + ? { type: 'colorMapping', value: state.layers[0].colorMapping } + : state.palette + ? { type: 'legacyPalette', value: state.palette } + : undefined; + }, getSuggestions: suggestions, @@ -176,6 +200,19 @@ export const getPieVisualization = ({ const datasource = frame.datasourceLayers[layer.layerId]; + let colors: string[] = []; + kibanaTheme.theme$ + .subscribe({ + next(theme) { + colors = state.layers[0]?.colorMapping + ? getPaletteColors(theme.darkMode, state.layers[0].colorMapping) + : paletteService + .get(state.palette?.name || 'default') + .getCategoricalColors(10, state.palette?.params); + }, + }) + .unsubscribe(); + const getPrimaryGroupConfig = (): VisualizationDimensionGroupConfig => { const originalOrder = getSortedAccessorsForGroup(datasource, layer, 'primaryGroups'); // When we add a column it could be empty, and therefore have no order @@ -189,9 +226,7 @@ export const getPieVisualization = ({ accessors.forEach((accessorConfig) => { if (firstNonCollapsedColumnId === accessorConfig.columnId) { accessorConfig.triggerIconType = 'colorBy'; - accessorConfig.palette = paletteService - .get(state.palette?.name || 'default') - .getCategoricalColors(10, state.palette?.params); + accessorConfig.palette = colors; } }); diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/suggestions.ts b/x-pack/plugins/lens/public/visualizations/tagcloud/suggestions.ts index c85f7b0b28fe2..4a528c99d41ad 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/suggestions.ts @@ -7,6 +7,7 @@ import { partition } from 'lodash'; import { IconChartTagcloud } from '@kbn/chart-icons'; +import { DEFAULT_COLOR_MAPPING_CONFIG } from '@kbn/coloring'; import type { SuggestionRequest, VisualizationSuggestion } from '../../types'; import type { TagcloudState } from './types'; import { DEFAULT_STATE, TAGCLOUD_LABEL } from './constants'; @@ -48,6 +49,11 @@ export function getSuggestions({ tagAccessor: bucket.columnId, valueAccessor: metrics[0].columnId, ...DEFAULT_STATE, + colorMapping: !mainPalette + ? { ...DEFAULT_COLOR_MAPPING_CONFIG } + : mainPalette?.type === 'colorMapping' + ? mainPalette.value + : undefined, }, }; }); diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index c68be83cae19f..97b368dde3a2c 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -93,7 +93,7 @@ export function TagsDimensionEditor({ > { @@ -103,7 +103,7 @@ export function TagsDimensionEditor({ setIsPaletteOpen(!isPaletteOpen)} title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { - defaultMessage: 'Color assignments', + defaultMessage: 'Color', })} >
diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index e833272b30db1..2e9b70d4054a8 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -259,7 +259,7 @@ export const getXyVisualization = ({ initialize( addNewLayer, state, - _mainPalette?, + mainPalette?, annotationGroups?: AnnotationGroups, references?: SavedObjectReference[] ) { @@ -281,7 +281,11 @@ export const getXyVisualization = ({ seriesType: defaultSeriesType, showGridlines: false, layerType: LayerTypes.DATA, - colorMapping: { ...DEFAULT_COLOR_MAPPING_CONFIG }, + palette: mainPalette?.type === 'legacyPalette' ? mainPalette.value : undefined, + colorMapping: + mainPalette?.type === 'colorMapping' + ? mainPalette.value + : { ...DEFAULT_COLOR_MAPPING_CONFIG }, }, ], } @@ -487,7 +491,13 @@ export const getXyVisualization = ({ getMainPalette: (state) => { if (!state || state.layers.length === 0) return; - return getFirstDataLayer(state.layers)?.palette; + const firstDataLayer = getFirstDataLayer(state.layers); + + return firstDataLayer?.colorMapping + ? { type: 'colorMapping', value: firstDataLayer.colorMapping } + : firstDataLayer?.palette + ? { type: 'legacyPalette', value: firstDataLayer.palette } + : undefined; }, getDropProps(dropProps) { diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index c0ac444526d34..5847de4196660 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -144,7 +144,12 @@ export function DataDimensionEditor( const localLayer: XYDataLayerConfig = layer; - const colors = getPaletteColors(props.darkMode, layer.colorMapping); + const colors = layer.colorMapping + ? getPaletteColors(props.darkMode, layer.colorMapping) + : props.paletteService + .get(layer.palette?.name || 'default') + .getCategoricalColors(10, layer.palette); + const table = props.frame.activeData?.[layer.layerId]; const { splitAccessor } = layer; const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); @@ -159,7 +164,7 @@ export function DataDimensionEditor( > { @@ -169,7 +174,7 @@ export function DataDimensionEditor( | Array> | undefined { const [buckets, values] = partition(table.columns, (col) => col.operation.isBucketed); @@ -230,7 +230,7 @@ function getSuggestionsForLayer({ tableLabel?: string; keptLayerIds: string[]; requestedSeriesType?: SeriesType; - mainPalette?: PaletteOutput; + mainPalette?: SuggestionRequest['mainPalette']; allowMixed?: boolean; }): VisualizationSuggestion | Array> { const title = getSuggestionTitle(yValues, xValue, tableLabel); @@ -493,7 +493,7 @@ function buildSuggestion({ changeType: TableChangeType; keptLayerIds: string[]; hide?: boolean; - mainPalette?: PaletteOutput; + mainPalette?: SuggestionRequest['mainPalette']; allowMixed?: boolean; }) { if (seriesType.includes('percentage') && xValue?.operation.scale === 'ordinal' && !splitBy) { @@ -502,13 +502,15 @@ function buildSuggestion({ } const existingLayer = getExistingLayer(currentState, layerId) || null; const accessors = yValues.map((col) => col.columnId); + console.log('XY suggestions, ', mainPalette); const newLayer: XYDataLayerConfig = { ...(existingLayer || {}), palette: - mainPalette || - (existingLayer && 'palette' in existingLayer + mainPalette?.type === 'legacyPalette' + ? mainPalette.value + : existingLayer && 'palette' in existingLayer ? (existingLayer as XYDataLayerConfig).palette - : undefined), + : undefined, layerId, seriesType, xAccessor: xValue?.columnId, @@ -519,6 +521,11 @@ function buildSuggestion({ ? existingLayer.yConfig.filter(({ forAccessor }) => accessors.indexOf(forAccessor) !== -1) : undefined, layerType: LayerTypes.DATA, + colorMapping: !mainPalette + ? { ...DEFAULT_COLOR_MAPPING_CONFIG } + : mainPalette?.type === 'colorMapping' + ? mainPalette.value + : undefined, }; const hasDateHistogramDomain = diff --git a/x-pack/test/functional/apps/lens/group4/colors.ts b/x-pack/test/functional/apps/lens/group4/colors.ts index 4078b0663d7ae..07dbec77d94a2 100644 --- a/x-pack/test/functional/apps/lens/group4/colors.ts +++ b/x-pack/test/functional/apps/lens/group4/colors.ts @@ -31,7 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', operation: 'terms', field: '@message.raw', - palette: 'negative', + palette: { mode: 'colorMapping', id: 'negative' }, keepOpen: true, }); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 5d3f316b7a83a..578a112025531 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -178,7 +178,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont field?: string; isPreviousIncompatible?: boolean; keepOpen?: boolean; - palette?: string; + palette?: { mode: 'legacy' | 'colorMapping'; id: string }; formula?: string; disableEmptyRows?: boolean; }) { @@ -205,7 +205,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } if (opts.palette) { - await this.setPalette(opts.palette); + await this.setPalette(opts.palette.id, opts.palette.mode === 'legacy'); } if (opts.disableEmptyRows) { @@ -519,13 +519,18 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await PageObjects.header.waitUntilLoadingHasFinished(); }, - async assertPalette(palette: string) { + async assertPalette(paletteId: string, isLegacy: boolean) { await retry.try(async () => { - await testSubjects.click('lns-palettePicker'); + await testSubjects.click('lns_colorEditing_trigger'); + if (isLegacy) { + await testSubjects.click('lns-palettePicker'); + } else { + await testSubjects.click('kbnColoring_ColorMapping_PalettePicker'); + } const currentPalette = await ( await find.byCssSelector('[role=option][aria-selected=true]') ).getAttribute('id'); - expect(currentPalette).to.equal(palette); + expect(currentPalette).to.equal(paletteId); }); }, @@ -1214,9 +1219,16 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await testSubjects.click(`${paletteName}-palette`); }, - async setPalette(paletteName: string) { - await testSubjects.click('lns-palettePicker'); - await find.clickByCssSelector(`#${paletteName}`); + async setPalette(paletteId: string, isLegacy: boolean) { + await testSubjects.click('lns_colorEditing_trigger'); + if (isLegacy) { + await testSubjects.click('lns-palettePicker'); + await find.clickByCssSelector(`#${paletteId}`); + } else { + await testSubjects.click('kbnColoring_ColorMapping_PalettePicker'); + await testSubjects.click(`kbnColoring_ColorMapping_Palette-${paletteId}`); + } + await testSubjects.click('lns-indexPattern-PalettePanelContainerBack'); }, async closePaletteEditor() { @@ -1456,7 +1468,6 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont dimension: string; field: string; keepOpen?: boolean; - palette?: string; }) { await retry.try(async () => { if (!(await testSubjects.exists('lns-indexPattern-dimensionContainerClose'))) { @@ -1467,10 +1478,6 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await this.selectOptionFromComboBox('text-based-dimension-field', opts.field); - if (opts.palette) { - await this.setPalette(opts.palette); - } - if (!opts.keepOpen) { await testSubjects.click('collapseFlyoutButton'); } From 297e6e104789a54abffc9bfdc376c6072f8f655b Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 6 Sep 2023 16:56:16 +0200 Subject: [PATCH 20/71] feat: add telemetry to color mapping switch --- .../visualizations/partition/dimension_editor.tsx | 10 ++++++---- .../visualizations/tagcloud/tags_dimension_editor.tsx | 10 ++++++---- .../lens/public/visualizations/xy/visualization.tsx | 1 + .../xy/xy_config_panel/dimension_editor.tsx | 10 ++++++---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index 8cd1d5b38cd9e..39afaf689c6df 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -36,6 +36,7 @@ import { hasNonCollapsedSliceBy, isCollapsed, } from './visualization'; +import { trackUiCounterEvents } from '../../lens_ui_telemetry'; type DimensionEditorProps = VisualizationDimensionEditorProps & { paletteService: PaletteRegistry; @@ -178,11 +179,12 @@ export function DimensionEditor(props: DimensionEditorProps) { label="Use new color mapping (tech preview)" compressed checked={useNewColorMapping} - onChange={(e) => { - setColorMapping( - e.target.checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + onChange={({ target: { checked } }) => { + trackUiCounterEvents( + `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` ); - setUseNewColorMapping(e.target.checked); + setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); + setUseNewColorMapping(checked); }} /> diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index 97b368dde3a2c..8e90f23f4b013 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -31,6 +31,7 @@ import { getColorCategories } from '@kbn/chart-expressions-common'; import type { TagcloudState } from './types'; import { PalettePanelContainer } from '../../shared_components'; import { FramePublicAPI } from '../../types'; +import { trackUiCounterEvents } from '../../lens_ui_telemetry'; interface Props { paletteService: PaletteRegistry; @@ -133,11 +134,12 @@ export function TagsDimensionEditor({ label="Use new color mapping (tech preview)" compressed checked={useNewColorMapping} - onChange={(e) => { - setColorMapping( - e.target.checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + onChange={({ target: { checked } }) => { + trackUiCounterEvents( + `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` ); - setUseNewColorMapping(e.target.checked); + setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); + setUseNewColorMapping(checked); }} /> diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 2e9b70d4054a8..60b95799b6939 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -669,6 +669,7 @@ export const getXyVisualization = ({ formatFactory: fieldFormats.deserialize, paletteService, }; + const darkMode: boolean = useObservable(kibanaTheme.theme$, { darkMode: false }).darkMode; const layer = props.state.layers.find((l) => l.layerId === props.layerId)!; const dimensionEditor = isReferenceLayer(layer) ? ( diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 5847de4196660..e11701a216322 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -40,6 +40,7 @@ import { getDataLayers } from '../visualization_helpers'; import { CollapseSetting } from '../../../shared_components/collapse_setting'; import { getSortedAccessors } from '../to_expression'; import { getColorAssignments, getAssignedColorConfig } from '../color_assignment'; +import { trackUiCounterEvents } from '../../../lens_ui_telemetry'; type UnwrapArray = T extends Array ? P : T; @@ -204,11 +205,12 @@ export function DataDimensionEditor( label="Use new color mapping (tech preview)" compressed checked={useNewColorMapping} - onChange={(e) => { - setColorMapping( - e.target.checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + onChange={({ target: { checked } }) => { + trackUiCounterEvents( + `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` ); - setUseNewColorMapping(e.target.checked); + setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); + setUseNewColorMapping(checked); }} /> From e9bc8305734b0e1a51218fef113e77db53a1c338 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 6 Sep 2023 18:27:58 +0200 Subject: [PATCH 21/71] fix errors on lints --- .../color_mapping/config/default_color_mapping.ts | 4 ++-- .../lens/public/visualizations/partition/to_expression.ts | 1 - .../lens/public/visualizations/partition/visualization.tsx | 1 - .../plugins/lens/public/visualizations/xy/xy_suggestions.ts | 1 - x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 7 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index f8a7cd7780a66..8e8ad294b6f4e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -12,7 +12,7 @@ import { NeutralPalette, getPalette, DEFAULT_NEUTRAL_PALETTE_INDEX, - IKEAPalette, + EUIPalette, } from '../palettes/default_palettes'; export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { @@ -31,7 +31,7 @@ export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { touched: false, }, ], - paletteId: IKEAPalette.id, + paletteId: EUIPalette.id, colorMode: { type: 'categorical', }, diff --git a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts index 177c6f154db25..29e5fd399d148 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/to_expression.ts @@ -175,7 +175,6 @@ const generateCommonArguments = ( const datasource = datasourceLayers[layer.layerId]; const columnToLabelMap = getColumnToLabelMap(layer.metrics, datasource); const sortedMetricAccessors = getSortedAccessorsForGroup(datasource, layer, 'metrics'); - console.log(state); return { labels: generateCommonLabelsAstArgs(state, attributes, layer, columnToLabelMap), buckets: operations diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index dea43a5847e0f..8196f66546cf6 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -162,7 +162,6 @@ export const getPieVisualization = ({ triggers: [VIS_EVENT_TO_TRIGGER.filter], initialize(addNewLayer, state, mainPalette) { - console.log('initialize', { state, mainPalette }); return ( state || { shape: PieChartTypes.DONUT, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts index 38c6bbb243092..b63acd9513300 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.ts @@ -502,7 +502,6 @@ function buildSuggestion({ } const existingLayer = getExistingLayer(currentState, layerId) || null; const accessors = yValues.map((col) => col.columnId); - console.log('XY suggestions, ', mainPalette); const newLayer: XYDataLayerConfig = { ...(existingLayer || {}), palette: diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 29fd5965bf442..66bb9d90fbd80 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21433,7 +21433,6 @@ "xpack.lens.table.dynamicColoring.text": "Texte", "xpack.lens.table.hide.hideLabel": "Masquer", "xpack.lens.table.palettePanelContainer.back": "Retour", - "xpack.lens.table.palettePanelTitle": "Couleur", "xpack.lens.table.resize.reset": "Réinitialiser la largeur", "xpack.lens.table.rowHeight.auto": "Ajustement automatique", "xpack.lens.table.rowHeight.custom": "Personnalisé", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 7c8ed5b0a5813..9c963c666d20a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21448,7 +21448,6 @@ "xpack.lens.table.dynamicColoring.text": "テキスト", "xpack.lens.table.hide.hideLabel": "非表示", "xpack.lens.table.palettePanelContainer.back": "戻る", - "xpack.lens.table.palettePanelTitle": "色", "xpack.lens.table.resize.reset": "幅のリセット", "xpack.lens.table.rowHeight.auto": "自動的に合わせる", "xpack.lens.table.rowHeight.custom": "カスタム", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b4899f5bd0b4c..4e520381a6db6 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21448,7 +21448,6 @@ "xpack.lens.table.dynamicColoring.text": "文本", "xpack.lens.table.hide.hideLabel": "隐藏", "xpack.lens.table.palettePanelContainer.back": "返回", - "xpack.lens.table.palettePanelTitle": "颜色", "xpack.lens.table.resize.reset": "重置宽度", "xpack.lens.table.rowHeight.auto": "自动适应", "xpack.lens.table.rowHeight.custom": "定制", From e7f62a9b6279ef2e42aaed814706ab10b8f66fe8 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 7 Sep 2023 11:06:51 +0200 Subject: [PATCH 22/71] add README --- .../shared_components/color_mapping/README.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/README.md diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/README.md b/packages/kbn-coloring/src/shared_components/color_mapping/README.md new file mode 100644 index 0000000000000..c9e563fb6dc30 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/README.md @@ -0,0 +1,84 @@ +# Color Mapping + +This shared component can be used to define a color mapping as an association of one or multiple string values to a color definition. + +This package provides: +- a React component, called `CategoricalColorMapping` that provides a simplified UI (that in general can be hosted in a flyout), that helps the user generate a `ColorMapping.Config` object that descibes the mappings configuration +- a function `getColorFactory` that given a color mapping configuration returns a function that maps a passed category to the corresponding color +- a definition scheme for the color mapping, based on the type `ColorMapping.Config`, that provides an extensible way of describing the link betwe + + +An example of a configuration is the following: +```ts +const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { + assignmentMode: 'auto', + assignments: [ + { + rule: { + type: 'matchExactly', + values: ['']; + }, + color: { + type: 'categorical', + paletteId: 'eui', + colorIndex: 2, + } + } + ], + specialAssignments: [ + { + rule: { + type: 'other', + }, + color: { + type: 'categorical', + paletteId: 'neutral', + colorIndex: 2 + }, + touched: false, + }, + ], + paletteId: EUIPalette.id, + colorMode: { + type: 'categorical', + }, +}; +``` + +The function `getColorFactory` has the following type: +```ts +function getColorFactory( + model: ColorMapping.Config, + getPaletteFn: (paletteId: string) => ColorMapping.CategoricalPalette, + isDarkMode: boolean, + data: { + type: 'categories'; + categories: Array; + } +): (category: string | string[]) => Color +``` +where given the model, a palette getter, the theme mode (dark/light) and a list of categories, it will return a function that can be used to pick the right color based on the passed category. + + +A `category` can be in the shape of a plain string (trimmed) or an array of strings. Numbers, MultiFieldKey, IP etc needs to be stringified. + + +The `CategoricalColorMapping` React component has the following props: + +```tsx +function CategoricalColorMapping(props: { + /** the initial color mapping model, usually coming from a the visualization saved object */ + model: ColorMapping.Config; + /** A map of paletteId and palette configuration */ + palettes: Map; + data: ColorMappingInputData; + isDarkMode: boolean; + /** map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ + specialTokens: Map; + /** a function called at every change in the model */ + onModelUpdate: (model: ColorMapping.Config) => void; +}) + +``` + +the `onModelUpdate` callback is called everytime a change in the model is applied from within the component. Is not called when the `model` prop is updated. \ No newline at end of file From 603139e957168aae110b0e373f7c1b4d0057137c Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 7 Sep 2023 11:38:48 +0200 Subject: [PATCH 23/71] fix unit tests --- .../treemap_vis_function.test.ts.snap | 1 + .../tagcloud_function.test.ts.snap | 2 ++ .../__snapshots__/xy_chart.test.tsx.snap | 10 ++++++ .../charts/public/services/theme/theme.ts | 2 +- .../partition/suggestions.test.ts | 13 +++++--- .../visualizations/xy/xy_suggestions.test.ts | 33 +++++++++++++++---- 6 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap index f6817eca439cf..17c372547ad79 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap @@ -71,6 +71,7 @@ Object { "type": "vis_dimension", }, ], + "colorMapping": undefined, "dimensions": Object { "buckets": Array [ Object { diff --git a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap index d03cebd680290..15f335df82684 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_tagcloud/common/expression_functions/__snapshots__/tagcloud_function.test.ts.snap @@ -66,6 +66,7 @@ Object { "bucket": Object { "accessor": 1, }, + "colorMapping": undefined, "isPreview": false, "maxFontSize": 72, "metric": Object { @@ -126,6 +127,7 @@ Object { }, "type": "vis_dimension", }, + "colorMapping": undefined, "isPreview": false, "maxFontSize": 72, "metric": Object { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index fe76259b65889..9bc59b677ed78 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -1099,6 +1099,7 @@ exports[`XYChart component it renders area 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -2107,6 +2108,7 @@ exports[`XYChart component it renders bar 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -3115,6 +3117,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -4123,6 +4126,7 @@ exports[`XYChart component it renders line 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -5131,6 +5135,7 @@ exports[`XYChart component it renders stacked area 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -6139,6 +6144,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -7147,6 +7153,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` }, } } + isDarkMode={false} layers={ Array [ Object { @@ -8381,6 +8388,7 @@ exports[`XYChart component split chart should render split chart if both, splitR }, } } + isDarkMode={false} layers={ Array [ Object { @@ -9622,6 +9630,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA }, } } + isDarkMode={false} layers={ Array [ Object { @@ -10861,6 +10870,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce }, } } + isDarkMode={false} layers={ Array [ Object { diff --git a/src/plugins/charts/public/services/theme/theme.ts b/src/plugins/charts/public/services/theme/theme.ts index 33c2163449ecd..dff300a02aa8a 100644 --- a/src/plugins/charts/public/services/theme/theme.ts +++ b/src/plugins/charts/public/services/theme/theme.ts @@ -12,7 +12,7 @@ import { Observable, BehaviorSubject } from 'rxjs'; import { CoreSetup, CoreTheme } from '@kbn/core/public'; import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; -console.log({ EUI_CHARTS_THEME_LIGHT, EUI_CHARTS_THEME_DARK }); + export class ThemeService { /** Returns default charts theme */ public readonly chartsDefaultTheme = EUI_CHARTS_THEME_LIGHT.theme; diff --git a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts index faba5edb73298..6486c1a95558a 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/partition/suggestions.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { PaletteOutput } from '@kbn/coloring'; +import { DEFAULT_COLOR_MAPPING_CONFIG, PaletteOutput } from '@kbn/coloring'; import { suggestions } from './suggestions'; import type { DataType, SuggestionRequest } from '../../types'; import type { PieLayerState, PieVisualizationState } from '../../../common/types'; @@ -594,7 +594,6 @@ describe('suggestions', () => { }); it('should keep passed in palette', () => { - const mainPalette: PaletteOutput = { type: 'palette', name: 'mock' }; const results = suggestions({ table: { layerId: 'first', @@ -617,10 +616,13 @@ describe('suggestions', () => { }, state: undefined, keptLayerIds: ['first'], - mainPalette, + mainPalette: { + type: 'legacyPalette', + value: { type: 'palette', name: 'mock' }, + }, }); - expect(results[0].state.palette).toEqual(mainPalette); + expect(results[0].state.palette).toEqual({ type: 'palette', name: 'mock' }); }); it('should keep the layer settings and palette when switching from treemap', () => { @@ -681,6 +683,7 @@ describe('suggestions', () => { legendMaxLines: 1, truncateLegend: true, nestedLegend: true, + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, }, ], }, @@ -1060,6 +1063,7 @@ describe('suggestions', () => { Object { "allowMultipleMetrics": false, "categoryDisplay": "default", + "colorMapping": undefined, "layerId": "first", "layerType": "data", "legendDisplay": "show", @@ -1169,6 +1173,7 @@ describe('suggestions', () => { "layers": Array [ Object { "categoryDisplay": "default", + "colorMapping": undefined, "layerId": "first", "layerType": "data", "legendDisplay": "show", diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts index 8417d02d79995..a12afc7465579 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_suggestions.test.ts @@ -18,7 +18,7 @@ import { generateId } from '../../id_generator'; import { getXyVisualization } from './xy_visualization'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks'; -import type { PaletteOutput } from '@kbn/coloring'; +import { DEFAULT_COLOR_MAPPING_CONFIG, PaletteOutput } from '@kbn/coloring'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; @@ -757,7 +757,7 @@ describe('xy_suggestions', () => { changeType: 'unchanged', }, keptLayerIds: [], - mainPalette, + mainPalette: { type: 'legacyPalette', value: mainPalette }, }); expect((suggestion.state.layers as XYDataLayerConfig[])[0].palette).toEqual(mainPalette); @@ -773,7 +773,7 @@ describe('xy_suggestions', () => { changeType: 'unchanged', }, keptLayerIds: [], - mainPalette, + mainPalette: { type: 'legacyPalette', value: mainPalette }, }); expect((suggestion.state.layers as XYDataLayerConfig[])[0].palette).toEqual(undefined); @@ -913,7 +913,13 @@ describe('xy_suggestions', () => { expect(suggestions[0].state).toEqual({ ...currentState, preferredSeriesType: 'line', - layers: [{ ...currentState.layers[0], seriesType: 'line' }], + layers: [ + { + ...currentState.layers[0], + seriesType: 'line', + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, + }, + ], }); expect(suggestions[0].title).toEqual('Line chart'); }); @@ -954,12 +960,24 @@ describe('xy_suggestions', () => { expect(seriesSuggestion.state).toEqual({ ...currentState, preferredSeriesType: 'line', - layers: [{ ...currentState.layers[0], seriesType: 'line' }], + layers: [ + { + ...currentState.layers[0], + seriesType: 'line', + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, + }, + ], }); expect(stackSuggestion.state).toEqual({ ...currentState, preferredSeriesType: 'bar_stacked', - layers: [{ ...currentState.layers[0], seriesType: 'bar_stacked' }], + layers: [ + { + ...currentState.layers[0], + seriesType: 'bar_stacked', + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, + }, + ], }); expect(seriesSuggestion.title).toEqual('Line chart'); expect(stackSuggestion.title).toEqual('Stacked'); @@ -1081,6 +1099,7 @@ describe('xy_suggestions', () => { ...currentState.layers[0], xAccessor: 'product', splitAccessor: 'category', + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, }, ], }); @@ -1126,6 +1145,7 @@ describe('xy_suggestions', () => { ...currentState.layers[0], xAccessor: 'category', splitAccessor: 'product', + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, }, ], }); @@ -1172,6 +1192,7 @@ describe('xy_suggestions', () => { ...currentState.layers[0], xAccessor: 'timestamp', splitAccessor: 'product', + colorMapping: DEFAULT_COLOR_MAPPING_CONFIG, }, ], }); From fcf0b60e594a8ef3052c00f1b462e79ad4f168d6 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 7 Sep 2023 11:55:42 +0200 Subject: [PATCH 24/71] fix functional test assert palette and set palette --- .../public/visualizations/partition/dimension_editor.tsx | 1 + x-pack/test/functional/apps/lens/group4/colors.ts | 6 +++--- x-pack/test/functional/page_objects/lens_page.ts | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index 39afaf689c6df..2b434b9adcd13 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -177,6 +177,7 @@ export function DimensionEditor(props: DimensionEditorProps) { { diff --git a/x-pack/test/functional/apps/lens/group4/colors.ts b/x-pack/test/functional/apps/lens/group4/colors.ts index 07dbec77d94a2..8e63d4f210690 100644 --- a/x-pack/test/functional/apps/lens/group4/colors.ts +++ b/x-pack/test/functional/apps/lens/group4/colors.ts @@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { keepOpen: true, }); - await PageObjects.lens.assertPalette('negative'); + await PageObjects.lens.assertPalette('negative', false); }); it('should carry over palette to the pie chart', async () => { @@ -43,7 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openDimensionEditor( 'lnsPie_sliceByDimensionPanel > lns-dimensionTrigger' ); - await PageObjects.lens.assertPalette('negative'); + await PageObjects.lens.assertPalette('negative', false); }); it('should carry palette back to the bar chart', async () => { @@ -51,7 +51,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openDimensionEditor( 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' ); - await PageObjects.lens.assertPalette('negative'); + await PageObjects.lens.assertPalette('negative', false); }); }); } diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 578a112025531..9d1b169368f35 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1221,6 +1221,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont async setPalette(paletteId: string, isLegacy: boolean) { await testSubjects.click('lns_colorEditing_trigger'); + await testSubjects.setEuiSwitch( + 'lns_colorMappingOrLegacyPalette_switch', + isLegacy ? 'uncheck' : 'check' + ); if (isLegacy) { await testSubjects.click('lns-palettePicker'); await find.clickByCssSelector(`#${paletteId}`); From 7d4aa4014e6f57035e3de4ea7bddeb1ef57b8a02 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 7 Sep 2023 12:02:52 +0200 Subject: [PATCH 25/71] fix missing/changed types --- .../editor_frame/workspace_panel/chart_switch.test.tsx | 5 ++++- .../lens/public/visualizations/gauge/visualization.tsx | 2 +- .../public/visualizations/legacy_metric/dimension_editor.tsx | 1 - .../lens/public/visualizations/metric/dimension_editor.tsx | 1 - .../lens/public/visualizations/metric/visualization.tsx | 2 +- .../xy/xy_config_panel/xy_config_panel.test.tsx | 5 +++++ 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx index 5b14bb43dfb16..c3014efb099eb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx @@ -519,7 +519,10 @@ describe('chart_switch', () => { it('should query main palette from active chart and pass into suggestions', async () => { const visualizationMap = mockVisualizationMap(); const mockPalette: PaletteOutput = { type: 'palette', name: 'mock' }; - visualizationMap.visA.getMainPalette = jest.fn(() => mockPalette); + visualizationMap.visA.getMainPalette = jest.fn(() => ({ + type: 'legacyPalette', + value: mockPalette, + })); visualizationMap.visB.getSuggestions.mockReturnValueOnce([]); const frame = mockFrame(['a', 'b', 'c']); const currentVisState = {}; diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index b943e9601f24b..ac78d37ca28e9 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -224,7 +224,7 @@ export const getGaugeVisualization = ({ layerId: addNewLayer(), layerType: LayerTypes.DATA, shape: GaugeShapes.HORIZONTAL_BULLET, - palette: mainPalette, + palette: mainPalette?.type === 'legacyPalette' ? mainPalette.value : undefined, ticksPosition: 'auto', labelMajorMode: 'auto', } diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx index 7e4669ffd1b3f..75500e5a3e9aa 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx @@ -188,7 +188,6 @@ export function MetricDimensionEditor( title={i18n.translate('xpack.lens.table.colorByRangePanelTitle', { defaultMessage: 'Color', })} - isTechPreview={false} > { addLayer={jest.fn()} removeLayer={jest.fn()} datasource={{} as DatasourcePublicAPI} + darkMode={false} /> ); @@ -299,6 +300,7 @@ describe('XY Config panels', () => { addLayer={jest.fn()} removeLayer={jest.fn()} datasource={{} as DatasourcePublicAPI} + darkMode={false} /> ); @@ -347,6 +349,7 @@ describe('XY Config panels', () => { addLayer={jest.fn()} removeLayer={jest.fn()} datasource={{} as DatasourcePublicAPI} + darkMode={false} /> ); @@ -392,6 +395,7 @@ describe('XY Config panels', () => { addLayer={jest.fn()} removeLayer={jest.fn()} datasource={{} as DatasourcePublicAPI} + darkMode={false} /> ); @@ -437,6 +441,7 @@ describe('XY Config panels', () => { addLayer={jest.fn()} removeLayer={jest.fn()} datasource={{} as DatasourcePublicAPI} + darkMode={false} /> ); From be1fe7a5ede6ed252dcffbfd2ddf0f77ccd60ffe Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 7 Sep 2023 13:17:18 +0200 Subject: [PATCH 26/71] fix snapshots --- .../mosaic_vis_function.test.ts.snap | 1 + .../pie_vis_function.test.ts.snap | 2 ++ .../waffle_vis_function.test.ts.snap | 1 + .../workspace_panel/chart_switch.test.tsx | 2 +- .../tagcloud/tags_dimension_editor.tsx | 1 + .../visualizations/xy/visualization.test.tsx | 22 +++++++++++++++++++ .../xy/xy_config_panel/dimension_editor.tsx | 1 + .../apps/dashboard/group2/sync_colors.ts | 2 ++ 8 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index 604368d7ab130..a3cd4f976757b 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -71,6 +71,7 @@ Object { "type": "vis_dimension", }, ], + "colorMapping": undefined, "dimensions": Object { "buckets": Array [ Object { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 293f86c6bf9ec..edcb2c8fd76e4 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -61,6 +61,7 @@ Object { "type": "vis_dimension", }, ], + "colorMapping": undefined, "dimensions": Object { "buckets": Array [ Object { @@ -203,6 +204,7 @@ Object { "type": "vis_dimension", }, ], + "colorMapping": undefined, "dimensions": Object { "buckets": Array [ Object { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap index 7c74291190a2d..cb1d724053dfe 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap @@ -53,6 +53,7 @@ Object { }, "type": "vis_dimension", }, + "colorMapping": undefined, "dimensions": Object { "buckets": Array [ Object { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx index c3014efb099eb..3e613d5a23e89 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx @@ -553,7 +553,7 @@ describe('chart_switch', () => { expect(visualizationMap.visB.getSuggestions).toHaveBeenCalledWith( expect.objectContaining({ keptLayerIds: ['a'], - mainPalette: mockPalette, + mainPalette: { type: 'legacyPalette', value: mockPalette }, }) ); }); diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index 8e90f23f4b013..43774b75a63d9 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -132,6 +132,7 @@ export function TagsDimensionEditor({ { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx index e89b3a843e8ac..ef48cbc06f693 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx @@ -221,8 +221,30 @@ describe('xy_visualization', () => { "layers": Array [ Object { "accessors": Array [], + "colorMapping": Object { + "assignmentMode": "auto", + "assignments": Array [], + "colorMode": Object { + "type": "categorical", + }, + "paletteId": "eui", + "specialAssignments": Array [ + Object { + "color": Object { + "colorIndex": 1, + "paletteId": "neutral", + "type": "categorical", + }, + "rule": Object { + "type": "other", + }, + "touched": false, + }, + ], + }, "layerId": "l1", "layerType": "data", + "palette": undefined, "position": "top", "seriesType": "bar_stacked", "showGridlines": false, diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index e11701a216322..e3194d1f538ff 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -203,6 +203,7 @@ export function DataDimensionEditor( { diff --git a/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts b/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts index 2692c40af0adf..30a7075207df3 100644 --- a/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts +++ b/x-pack/test/functional/apps/dashboard/group2/sync_colors.ts @@ -68,6 +68,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', operation: 'terms', field: 'geo.src', + palette: { mode: 'legacy', id: 'default' }, }); await PageObjects.lens.save('vis1', false, true); @@ -84,6 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', operation: 'terms', field: 'geo.src', + palette: { mode: 'legacy', id: 'default' }, }); await filterBar.addFilter({ field: 'geo.src', operation: 'is not', value: 'CN' }); From 701de66e81cfa2e0ce606968b3322e82dec7c314 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 7 Sep 2023 16:36:36 +0200 Subject: [PATCH 27/71] fix functional tests --- .../tagcloud_renderer.tsx | 7 +++- .../functional/apps/lens/group4/colors.ts | 37 ++++++++++++++++--- .../test/functional/page_objects/lens_page.ts | 10 ++++- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx index fd39f6c1287d9..101c40b6b384d 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/expression_renderers/tagcloud_renderer.tsx @@ -67,7 +67,12 @@ export const tagcloudRenderer: ( }; const palettesRegistry = await plugins.charts.palettes.getPalettes(); - const isDarkMode = plugins.charts.theme.useDarkMode(); + let isDarkMode = false; + plugins.charts.theme.darkModeEnabled$ + .subscribe((val) => { + isDarkMode = val.darkMode; + }) + .unsubscribe(); render( diff --git a/x-pack/test/functional/apps/lens/group4/colors.ts b/x-pack/test/functional/apps/lens/group4/colors.ts index 8e63d4f210690..56c8bfff753cf 100644 --- a/x-pack/test/functional/apps/lens/group4/colors.ts +++ b/x-pack/test/functional/apps/lens/group4/colors.ts @@ -10,7 +10,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['visualize', 'lens', 'common']); describe('lens color palette tests', () => { - it('should allow to pick color palette in xy chart', async () => { + it('should allow to pick legacy color palette in xy chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); await PageObjects.lens.goToTimeRange(); @@ -31,11 +31,38 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', operation: 'terms', field: '@message.raw', - palette: { mode: 'colorMapping', id: 'negative' }, + palette: { mode: 'legacy', id: 'negative' }, keepOpen: true, }); - await PageObjects.lens.assertPalette('negative', false); + await PageObjects.lens.assertPalette('negative', true); + }); + it('should allow to pick color mapping palette in xy chart', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: 'geo.src', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'average', + field: 'bytes', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: '@message.raw', + palette: { mode: 'colorMapping', id: 'pastel' }, + keepOpen: true, + }); + + await PageObjects.lens.assertPalette('pastel', false); }); it('should carry over palette to the pie chart', async () => { @@ -43,7 +70,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openDimensionEditor( 'lnsPie_sliceByDimensionPanel > lns-dimensionTrigger' ); - await PageObjects.lens.assertPalette('negative', false); + await PageObjects.lens.assertPalette('pastel', false); }); it('should carry palette back to the bar chart', async () => { @@ -51,7 +78,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openDimensionEditor( 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' ); - await PageObjects.lens.assertPalette('negative', false); + await PageObjects.lens.assertPalette('pastel', false); }); }); } diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 9d1b169368f35..9d8a9a7ecec7f 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -522,6 +522,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont async assertPalette(paletteId: string, isLegacy: boolean) { await retry.try(async () => { await testSubjects.click('lns_colorEditing_trigger'); + // open the palette picker if (isLegacy) { await testSubjects.click('lns-palettePicker'); } else { @@ -530,7 +531,14 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont const currentPalette = await ( await find.byCssSelector('[role=option][aria-selected=true]') ).getAttribute('id'); + // close the palette picker + if (isLegacy) { + await testSubjects.click('lns-palettePicker'); + } else { + await testSubjects.click('kbnColoring_ColorMapping_PalettePicker'); + } expect(currentPalette).to.equal(paletteId); + await this.closePaletteEditor(); }); }, @@ -1232,7 +1240,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await testSubjects.click('kbnColoring_ColorMapping_PalettePicker'); await testSubjects.click(`kbnColoring_ColorMapping_Palette-${paletteId}`); } - await testSubjects.click('lns-indexPattern-PalettePanelContainerBack'); + await this.closePaletteEditor(); }, async closePaletteEditor() { From 1c1e0ae93e48484a46c9245e9411a819925b8a5f Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 8 Sep 2023 09:40:32 +0200 Subject: [PATCH 28/71] fix eslint --- packages/kbn-coloring/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kbn-coloring/tsconfig.json b/packages/kbn-coloring/tsconfig.json index 54c068f8bd3b6..4ad624b0780db 100644 --- a/packages/kbn-coloring/tsconfig.json +++ b/packages/kbn-coloring/tsconfig.json @@ -10,7 +10,6 @@ ] }, "include": [ - "**/*.scss", "**/*.ts", "**/*.tsx" ], From 2943cec870d01ad26561a9a46f173be8042c0cda Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 8 Sep 2023 10:44:58 +0200 Subject: [PATCH 29/71] fix eslint --- .../snapshots/baseline/partial_test_1.json | 2 +- .../snapshots/baseline/tagcloud_all_data.json | 2 +- .../snapshots/baseline/tagcloud_empty_data.json | 2 +- .../snapshots/baseline/tagcloud_fontsize.json | 2 +- .../snapshots/session/partial_test_1.json | 2 +- .../snapshots/session/partial_test_2.json | 1 - .../snapshots/session/tagcloud_all_data.json | 2 +- .../snapshots/session/tagcloud_empty_data.json | 2 +- .../snapshots/session/tagcloud_fontsize.json | 2 +- .../snapshots/session/tagcloud_metric_data.json | 2 +- .../snapshots/session/tagcloud_options.json | 2 +- 11 files changed, 10 insertions(+), 11 deletions(-) delete mode 100644 test/interpreter_functional/snapshots/session/partial_test_2.json diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_1.json b/test/interpreter_functional/snapshots/baseline/partial_test_1.json index c7bb37566b0fe..90528b3321d22 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_1.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_1.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"isPreview":false,"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json index ac809c756d2cc..4d94b530c86e2 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json index 6b52c8de57ae5..8c4e9fc5cd523 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visParams":{"ariaLabel":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visParams":{"ariaLabel":null,"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json index 21e213ebb9c27..f142588711a31 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"isPreview":false,"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_1.json b/test/interpreter_functional/snapshots/session/partial_test_1.json index 6e12a10d1e283..90528b3321d22 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_1.json +++ b/test/interpreter_functional/snapshots/session/partial_test_1.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json deleted file mode 100644 index 922d266f7e187..0000000000000 --- a/test/interpreter_functional/snapshots/session/partial_test_2.json +++ /dev/null @@ -1 +0,0 @@ -{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_all_data.json b/test/interpreter_functional/snapshots/session/tagcloud_all_data.json index cb14c6ea89407..4d94b530c86e2 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_empty_data.json b/test/interpreter_functional/snapshots/session/tagcloud_empty_data.json index 0910e67409423..8c4e9fc5cd523 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_empty_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_empty_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visParams":{"ariaLabel":null,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[],"type":"datatable"},"visParams":{"ariaLabel":null,"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json index 21e213ebb9c27..f142588711a31 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"isPreview":false,"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json index f340c5b653e35..291e6b40e6bfd 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_options.json b/test/interpreter_functional/snapshots/session/tagcloud_options.json index ecbafbbc0afba..381d3afc54067 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file From c9a0f919257e679a8525b4cd3fc4f7e95c4d719a Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 8 Sep 2023 10:49:59 +0200 Subject: [PATCH 30/71] fix FTR expressions --- .../snapshots/baseline/tagcloud_metric_data.json | 2 +- .../snapshots/baseline/tagcloud_options.json | 2 +- .../snapshots/session/partial_test_2.json | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 test/interpreter_functional/snapshots/session/partial_test_2.json diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json index afaac18bf342d..291e6b40e6bfd 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json index 03f74cc01d3e3..381d3afc54067 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"colorMapping":null,"isPreview":false,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json new file mode 100644 index 0000000000000..922d266f7e187 --- /dev/null +++ b/test/interpreter_functional/snapshots/session/partial_test_2.json @@ -0,0 +1 @@ +{"as":"legacyMetricVis","type":"render","value":{"canNavigateToLens":false,"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"excludeIsRegex":true,"field":"response.raw","includeIsRegex":true,"missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"emptyAsNull":false},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"meta":{"source":"logstash-*","statistics":{"totalCount":14004},"type":"esaggs"},"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file From 3259f0b19d64f3a5c58383d8beaa566198920c2f Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 8 Sep 2023 16:09:54 +0200 Subject: [PATCH 31/71] increase bundle limit --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ce349c219dddc..2344cf1b5277e 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -49,7 +49,7 @@ pageLoadAssetSize: expressionLegacyMetricVis: 23121 expressionMetric: 22238 expressionMetricVis: 23121 - expressionPartitionVis: 26338 + expressionPartitionVis: 27000 expressionRepeatImage: 22341 expressionRevealImage: 25675 expressions: 140958 From ffb01f37330dc7edd74371a2dda1be865ca5e434 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 8 Sep 2023 16:58:31 +0200 Subject: [PATCH 32/71] add i18n --- .../components/assignment/assignment.tsx | 5 +- .../components/assignment/match.tsx | 10 +- .../assignment/special_assignment.tsx | 10 +- .../components/color_picker/color_picker.tsx | 14 ++- .../components/color_picker/color_swatch.tsx | 9 +- .../components/color_picker/hsl_picker.tsx | 101 ------------------ .../color_picker/palette_colors.tsx | 13 ++- .../components/color_picker/rgb_picker.tsx | 16 ++- .../components/container/container.tsx | 13 ++- .../palette_selector/palette_selector.tsx | 61 ++++++++--- .../color_mapping/config/assignments.ts | 3 +- .../config/default_color_mapping.ts | 12 +-- .../color_mapping/config/types.ts | 39 ++++++- .../palettes/default_palettes.ts | 4 - 14 files changed, 156 insertions(+), 154 deletions(-) delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/hsl_picker.tsx diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index 0ea063741b8cd..81d546329b368 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { removeAssignment, updateAssignmentColor, @@ -110,7 +111,9 @@ export function Assignment({ size="xs" disabled={disableDelete} onClick={() => dispatch(removeAssignment(index))} - aria-label="Delete assignment" + aria-label={i18n.translate('kbnColorMapping.assignments.deleteAssignmentButtonLabel', { + defaultMessage: 'Delete this assignment', + })} /> diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index 37fa220e9a1f6..c397799a483be 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiComboBox, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; // TODO: move this outside or configurable export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; @@ -53,8 +54,13 @@ export const Match: React.FC<{ data-test-subj={`lns-colorMapping-assignmentsItem${index}`} isDisabled={!editable} fullWidth={true} - aria-label="Accessible screen reader label" - placeholder="auto assigned term" + aria-label={i18n.translate('kbnColorMapping.assignments.autoAssignedTermAriaLabel', { + defaultMessage: + "This color will be automatically assigned to the first term that doesn't match with all the other assignments", + })} + placeholder={i18n.translate('kbnColorMapping.assignments.autoAssignedTermPlaceholder', { + defaultMessage: 'Auto assigned', + })} options={convertedOptions} selectedOptions={selectedOptions} onChange={(changedOptions) => { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx index f304d7f93fab5..93d97fc909542 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -9,6 +9,7 @@ import { EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useDispatch } from 'react-redux'; import React from 'react'; +import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; import { getPalette } from '../../palette'; import { ColorSwatch } from '../color_picker/color_swatch'; @@ -58,8 +59,13 @@ export function SpecialAssignment({ compressed fullWidth disabled={true} - placeholder={'all others'} - aria-label="Use aria labels when no actual label is in use" + placeholder={i18n.translate('kbnColorMapping.assignments.allOtherPlaceholder', { + defaultMessage: 'All other terms', + })} + aria-label={i18n.translate('kbnColorMapping.assignments.allOtherAriaLabel', { + defaultMessage: + 'Assign this color to all other terms not described in the assignment list', + })} /> diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx index 88ab02a9f686e..c0412f83c7ac8 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx @@ -8,6 +8,7 @@ import React, { useState } from 'react'; import { EuiButton, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; import { NeutralPalette } from '../../palettes/default_palettes'; import { getPalette } from '../../palette'; @@ -42,10 +43,14 @@ export function ColorPicker({
setTab('palette')} isSelected={tab === 'palette'}> - Palette + {i18n.translate('kbnColorMapping.colorPicker.paletteTabLabel', { + defaultMessage: 'Palette', + })} setTab('custom')} isSelected={tab === 'custom'}> - Custom + {i18n.translate('kbnColorMapping.colorPicker.customTabLabel', { + defaultMessage: 'Custom', + })} @@ -76,9 +81,10 @@ export function ColorPicker({ close(); deleteStep(); }} - aria-label="Delete color" > - Remove from gradient + {i18n.translate('kbnColorMapping.colorPicker.removeGradientColorButtonLabel', { + defaultMessage: 'Remove from gradient', + })} ) : null} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index bc3e6618fbec6..74e96926b2a8f 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -9,6 +9,7 @@ import { EuiColorPickerSwatch, EuiPopover } from '@elastic/eui'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { i18n } from '@kbn/i18n'; import { ColorPicker } from './color_picker'; import { getAssignmentColor } from '../../color/color_handling'; import { ColorMapping } from '../../config'; @@ -68,7 +69,9 @@ export const ColorSwatch = ({ button={ dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} style={{ ...(swatchShape === 'round' ? { borderRadius: '50%', width: 15, height: 15 } : {}), @@ -109,7 +112,9 @@ export const ColorSwatch = ({ ) : ( ; - selectColor: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; - close: () => void; -}) { - const [customColor, setCustomColor] = useState< - ColorMapping.CategoricalColor | ColorMapping.ColorCode - >(color); - - const customColorHex = - customColor.type === 'categorical' - ? getPaletteFn(customColor.paletteId).getColor(customColor.colorIndex, isDarkMode) - : customColor.colorCode; - return ( - - - - Contrast:{' '} - {getColorContrast(customColorHex, isDarkMode).contrast ? ( - - ) : ( - - )} -
- APCA: - {getColorContrast(customColorHex, isDarkMode).value} -
-
- - { - setCustomColor({ - type: 'colorCode', - colorCode: c, - }); - }} - color={customColorHex} - display="inline" - swatches={[]} - showAlpha - /> - - - - { - if (color !== customColor) { - selectColor(customColor); - } else { - close(); - } - }} - > - Apply this color - - - - { - close(); - }} - > - Close - - -
- ); -} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx index b3f1874b6e062..dc539bbb0f11c 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx @@ -7,6 +7,7 @@ */ import React from 'react'; import { EuiColorPickerSwatch, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; import { NeutralPalette } from '../../palettes/default_palettes'; import { isSameColor } from '../../color/color_math'; @@ -41,7 +42,11 @@ export function PaletteColors({ - Palette + + {i18n.translate('kbnColorMapping.colorPicker.paletteColorsLabel', { + defaultMessage: 'Palette', + })} + - Greys (theme aware) + + {i18n.translate('kbnColorMapping.colorPicker.themeAwareColorsLabel', { + defaultMessage: 'Contrast-aware greys', + })} +
- APCA: - {lightContrast.value} -
Dark Theme Contrast:{' '} -
- APCA: - {darkContrast.value} -
@@ -95,7 +89,9 @@ export function RGBPicker({ } }} > - Apply this color + {i18n.translate('kbnColorMapping.colorPicker.useColorButtonLabel', { + defaultMessage: 'Use this color', + })} @@ -105,7 +101,9 @@ export function RGBPicker({ close(); }} > - Close + {i18n.translate('kbnColorMapping.colorPicker.closePickerButtonLabel', { + defaultMessage: 'Close', + })}
diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index c1d6500d4ae49..b9eb8149bc6e8 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -17,6 +17,7 @@ import { EuiSpacer, EuiSwitch, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Assignment } from '../assignment/assignment'; import { SpecialAssignment } from '../assignment/special_assignment'; import { PaletteSelector } from '../palette_selector/palette_selector'; @@ -88,7 +89,9 @@ export function Container(props: { { @@ -184,7 +187,9 @@ export function Container(props: { }} disabled={!canAddNewAssignment} > - Add assignment + {i18n.translate('kbnColorMapping.container.addAssignmentButtonLabel', { + defaultMessage: 'Add assignment', + })} {colorMode.type === 'gradient' && ( - Invert gradient + {i18n.translate('kbnColorMapping.container.invertGradientButtonLabel', { + defaultMessage: 'Invert gradient', + })} )} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index a70c132daebd9..58474e53d2a4a 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -17,6 +17,7 @@ import { EuiFormRow, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { RootState, updatePalette } from '../../state/color_mapping'; import { ColorMapping } from '../../config'; import { updateAssignmentsPalette, updateColorModePalette } from '../../config/assignments'; @@ -97,7 +98,9 @@ export function PaletteSelector({ const preserveChangesModal = preserveModalPaletteId !== null ? ( { if (preserveModalPaletteId) switchPaletteFn(preserveModalPaletteId, true); setPreserveModalPaletteId(null); @@ -106,12 +109,20 @@ export function PaletteSelector({ if (preserveModalPaletteId) switchPaletteFn(preserveModalPaletteId, false); setPreserveModalPaletteId(null); }} - confirmButtonText="Discard changes" - cancelButtonText="Preserve changes" + confirmButtonText={i18n.translate('kbnColorMapping.colorChangesModal.discardButton', { + defaultMessage: 'Discard changes', + })} + cancelButtonText={i18n.translate('kbnColorMapping.colorChangesModal.preserveButton', { + defaultMessage: 'Preserve changes', + })} buttonColor="danger" defaultFocusedButton="confirm" > -

Switching palette will discard all your custom color changes

+

+ {i18n.translate('kbnColorMapping.colorChangesModal.switchPaletteDescription', { + defaultMessage: 'Switching palette will discard all your custom color changes', + })} +

) : null; @@ -122,7 +133,9 @@ export function PaletteSelector({ const colorScaleModal = colorScaleModalId !== null ? ( { setColorScaleModalId(null); }} @@ -130,12 +143,24 @@ export function PaletteSelector({ if (colorScaleModalId) updateColorMode(colorScaleModalId, false); setColorScaleModalId(null); }} - cancelButtonText="Go back" - confirmButtonText="Discard changes" + cancelButtonText={i18n.translate('kbnColorMapping.colorChangesModal.goBackButtonLabel', { + defaultMessage: 'Go back', + })} + confirmButtonText={i18n.translate('kbnColorMapping.colorChangesModal.discardButtonLabel', { + defaultMessage: 'Discard changes', + })} defaultFocusedButton="confirm" buttonColor="danger" > -

Switching to {colorScaleModalId} mode will discard all your custom color changes

+

+ {colorScaleModalId === 'categorical' + ? i18n.translate('kbnColorMapping.colorChangesModal.categoricalModeDescription', { + defaultMessage: `Switching to a categorical mode will discard all your custom color changes`, + }) + : i18n.translate('kbnColorMapping.colorChangesModal.sequentialModeDescription', { + defaultMessage: `Switching to a sequential mode will discard all your custom color changes`, + })} +

) : null; @@ -145,7 +170,11 @@ export function PaletteSelector({ {colorScaleModal} - + - + ; } +/** + * A Match rule to match the values case insensitive + * @ignore not used yet + */ export interface RuleMatchExactlyCI { /* tag */ type: 'matchExactlyCI'; values: string[]; } +/** + * A range rule, not used yet, but can be used for numerical data assignments + */ export interface RuleRange { /* tag */ type: 'range'; @@ -61,6 +81,10 @@ export interface RuleRange { */ maxInclusive: boolean; } +/** + * Regex rule. + * @ignore not used yet + */ export interface RuleRegExp { /* tag */ type: 'regex'; @@ -70,12 +94,17 @@ export interface RuleRegExp { values: string; } -// TODO: add RulerForSomeOtherFormOfObject +/** + * A specific catch-everything-else rule + */ export interface RuleOthers { /* tag */ type: 'other'; } +/** + * An assignment is the connection link between a rule and a color + */ export interface Assignment { /** * Describe the rule used to assign the color. @@ -117,5 +146,5 @@ export interface CategoricalPalette { name: string; type: 'categorical'; colorCount: number; - getColor: (valueInRange: number, isDarkMode: boolean) => Color; + getColor: (valueInRange: number, isDarkMode: boolean) => string; } diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts index cd9a7b1dc9820..a712c6a1f7f48 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts @@ -14,8 +14,6 @@ import { NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from './neutral'; import { ColorMapping } from '../config'; -export const DEFAULT_MISSING_COLOR = 'red'; - export const EUIPalette: ColorMapping.CategoricalPalette = { id: 'eui', name: 'EUI', @@ -68,8 +66,6 @@ export const NeutralPalette: ColorMapping.CategoricalPalette = { }, }; -export const DEFAULT_NEUTRAL_PALETTE_INDEX = 1; - export function getPalette( palettes: Map, defaultPalette: ColorMapping.CategoricalPalette From d94aa1bb9e696a16861363f332a8445370847971 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 8 Sep 2023 18:44:13 +0200 Subject: [PATCH 33/71] fix wrong import --- .../color_mapping/color/color_handling.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts index 3bdcf0118c035..0a89360d3e0c7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts @@ -6,15 +6,14 @@ * Side Public License, v 1. */ -import { DEFAULT_COLOR_MAPPING_CONFIG } from '../config/default_color_mapping'; +import { + DEFAULT_COLOR_MAPPING_CONFIG, + DEFAULT_NEUTRAL_PALETTE_INDEX, +} from '../config/default_color_mapping'; import { getColorFactory } from './color_handling'; import { getPalette } from '../palette'; import { AVAILABLE_PALETTES } from '../palettes/available_palettes'; -import { - DEFAULT_NEUTRAL_PALETTE_INDEX, - EUIPalette, - NeutralPalette, -} from '../palettes/default_palettes'; +import { EUIPalette, NeutralPalette } from '../palettes/default_palettes'; import { EUI_PALETTE_COLORS_DARK, EUI_PALETTE_COLORS_LIGHT } from '../palettes/eui'; import { NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from '../palettes/neutral'; import { toHex } from './color_math'; From b87d5636c7d335baee13f34f3ec4428165e0ea7a Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 11 Sep 2023 10:01:11 +0200 Subject: [PATCH 34/71] change i18n namespace --- .../components/assignment/assignment.tsx | 9 +++-- .../components/assignment/match.tsx | 11 +++-- .../assignment/special_assignment.tsx | 4 +- .../components/color_picker/color_picker.tsx | 6 +-- .../components/color_picker/color_swatch.tsx | 4 +- .../color_picker/palette_colors.tsx | 4 +- .../components/color_picker/rgb_picker.tsx | 4 +- .../components/container/container.tsx | 6 +-- .../palette_selector/palette_selector.tsx | 40 +++++++++++-------- 9 files changed, 50 insertions(+), 38 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index 81d546329b368..125caf9352e09 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -111,9 +111,12 @@ export function Assignment({ size="xs" disabled={disableDelete} onClick={() => dispatch(removeAssignment(index))} - aria-label={i18n.translate('kbnColorMapping.assignments.deleteAssignmentButtonLabel', { - defaultMessage: 'Delete this assignment', - })} + aria-label={i18n.translate( + 'coloring.colorMapping.assignments.deleteAssignmentButtonLabel', + { + defaultMessage: 'Delete this assignment', + } + )} /> diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index c397799a483be..727c077720482 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -54,13 +54,16 @@ export const Match: React.FC<{ data-test-subj={`lns-colorMapping-assignmentsItem${index}`} isDisabled={!editable} fullWidth={true} - aria-label={i18n.translate('kbnColorMapping.assignments.autoAssignedTermAriaLabel', { + aria-label={i18n.translate('coloring.colorMapping.assignments.autoAssignedTermAriaLabel', { defaultMessage: "This color will be automatically assigned to the first term that doesn't match with all the other assignments", })} - placeholder={i18n.translate('kbnColorMapping.assignments.autoAssignedTermPlaceholder', { - defaultMessage: 'Auto assigned', - })} + placeholder={i18n.translate( + 'coloring.colorMapping.assignments.autoAssignedTermPlaceholder', + { + defaultMessage: 'Auto assigned', + } + )} options={convertedOptions} selectedOptions={selectedOptions} onChange={(changedOptions) => { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx index 93d97fc909542..e5bd3afc2857c 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -59,10 +59,10 @@ export function SpecialAssignment({ compressed fullWidth disabled={true} - placeholder={i18n.translate('kbnColorMapping.assignments.allOtherPlaceholder', { + placeholder={i18n.translate('coloring.colorMapping.assignments.allOtherPlaceholder', { defaultMessage: 'All other terms', })} - aria-label={i18n.translate('kbnColorMapping.assignments.allOtherAriaLabel', { + aria-label={i18n.translate('coloring.colorMapping.assignments.allOtherAriaLabel', { defaultMessage: 'Assign this color to all other terms not described in the assignment list', })} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx index c0412f83c7ac8..a97a8df1611f2 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx @@ -43,12 +43,12 @@ export function ColorPicker({
setTab('palette')} isSelected={tab === 'palette'}> - {i18n.translate('kbnColorMapping.colorPicker.paletteTabLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.paletteTabLabel', { defaultMessage: 'Palette', })} setTab('custom')} isSelected={tab === 'custom'}> - {i18n.translate('kbnColorMapping.colorPicker.customTabLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.customTabLabel', { defaultMessage: 'Custom', })} @@ -82,7 +82,7 @@ export function ColorPicker({ deleteStep(); }} > - {i18n.translate('kbnColorMapping.colorPicker.removeGradientColorButtonLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.removeGradientColorButtonLabel', { defaultMessage: 'Remove from gradient', })} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index 74e96926b2a8f..c96af99e1c792 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -69,7 +69,7 @@ export const ColorSwatch = ({ button={ dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} @@ -112,7 +112,7 @@ export const ColorSwatch = ({ ) : ( - {i18n.translate('kbnColorMapping.colorPicker.paletteColorsLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.paletteColorsLabel', { defaultMessage: 'Palette', })} @@ -74,7 +74,7 @@ export function PaletteColors({ - {i18n.translate('kbnColorMapping.colorPicker.themeAwareColorsLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.themeAwareColorsLabel', { defaultMessage: 'Contrast-aware greys', })} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx index 6f5257ede5b31..494eec3049e91 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -89,7 +89,7 @@ export function RGBPicker({ } }} > - {i18n.translate('kbnColorMapping.colorPicker.useColorButtonLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.useColorButtonLabel', { defaultMessage: 'Use this color', })} @@ -101,7 +101,7 @@ export function RGBPicker({ close(); }} > - {i18n.translate('kbnColorMapping.colorPicker.closePickerButtonLabel', { + {i18n.translate('coloring.colorMapping.colorPicker.closePickerButtonLabel', { defaultMessage: 'Close', })} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index b9eb8149bc6e8..5f0b96891cc78 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -89,7 +89,7 @@ export function Container(props: { - {i18n.translate('kbnColorMapping.container.addAssignmentButtonLabel', { + {i18n.translate('coloring.colorMapping.container.addAssignmentButtonLabel', { defaultMessage: 'Add assignment', })} @@ -200,7 +200,7 @@ export function Container(props: { dispatch(changeGradientSortOrder(colorMode.sort === 'asc' ? 'desc' : 'asc')); }} > - {i18n.translate('kbnColorMapping.container.invertGradientButtonLabel', { + {i18n.translate('coloring.colorMapping.container.invertGradientButtonLabel', { defaultMessage: 'Invert gradient', })} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index 58474e53d2a4a..d93ed985abea6 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -98,7 +98,7 @@ export function PaletteSelector({ const preserveChangesModal = preserveModalPaletteId !== null ? ( { @@ -109,17 +109,17 @@ export function PaletteSelector({ if (preserveModalPaletteId) switchPaletteFn(preserveModalPaletteId, false); setPreserveModalPaletteId(null); }} - confirmButtonText={i18n.translate('kbnColorMapping.colorChangesModal.discardButton', { + confirmButtonText={i18n.translate('coloring.colorMapping.colorChangesModal.discardButton', { defaultMessage: 'Discard changes', })} - cancelButtonText={i18n.translate('kbnColorMapping.colorChangesModal.preserveButton', { + cancelButtonText={i18n.translate('coloring.colorMapping.colorChangesModal.preserveButton', { defaultMessage: 'Preserve changes', })} buttonColor="danger" defaultFocusedButton="confirm" >

- {i18n.translate('kbnColorMapping.colorChangesModal.switchPaletteDescription', { + {i18n.translate('coloring.colorMapping.colorChangesModal.switchPaletteDescription', { defaultMessage: 'Switching palette will discard all your custom color changes', })}

@@ -133,7 +133,7 @@ export function PaletteSelector({ const colorScaleModal = colorScaleModalId !== null ? ( { @@ -143,21 +143,27 @@ export function PaletteSelector({ if (colorScaleModalId) updateColorMode(colorScaleModalId, false); setColorScaleModalId(null); }} - cancelButtonText={i18n.translate('kbnColorMapping.colorChangesModal.goBackButtonLabel', { - defaultMessage: 'Go back', - })} - confirmButtonText={i18n.translate('kbnColorMapping.colorChangesModal.discardButtonLabel', { - defaultMessage: 'Discard changes', - })} + cancelButtonText={i18n.translate( + 'coloring.colorMapping.colorChangesModal.goBackButtonLabel', + { + defaultMessage: 'Go back', + } + )} + confirmButtonText={i18n.translate( + 'coloring.colorMapping.colorChangesModal.discardButtonLabel', + { + defaultMessage: 'Discard changes', + } + )} defaultFocusedButton="confirm" buttonColor="danger" >

{colorScaleModalId === 'categorical' - ? i18n.translate('kbnColorMapping.colorChangesModal.categoricalModeDescription', { + ? i18n.translate('coloring.colorMapping.colorChangesModal.categoricalModeDescription', { defaultMessage: `Switching to a categorical mode will discard all your custom color changes`, }) - : i18n.translate('kbnColorMapping.colorChangesModal.sequentialModeDescription', { + : i18n.translate('coloring.colorMapping.colorChangesModal.sequentialModeDescription', { defaultMessage: `Switching to a sequential mode will discard all your custom color changes`, })}

@@ -171,7 +177,7 @@ export function PaletteSelector({ @@ -206,7 +212,7 @@ export function PaletteSelector({ @@ -216,13 +222,13 @@ export function PaletteSelector({ options={[ { id: `categorical`, - label: i18n.translate('kbnColorMapping.paletteSelector.categoricalLabel', { + label: i18n.translate('coloring.colorMapping.paletteSelector.categoricalLabel', { defaultMessage: `Categorical`, }), }, { id: `gradient`, - label: i18n.translate('kbnColorMapping.paletteSelector.sequentialLabel', { + label: i18n.translate('coloring.colorMapping.paletteSelector.sequentialLabel', { defaultMessage: `Sequential`, }), }, From db6c45180fb0561a5dba1e9a9c33794f938d0f47 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 11 Sep 2023 13:08:20 +0200 Subject: [PATCH 35/71] add simple FTs --- .../components/color_picker/color_swatch.tsx | 1 + .../color_picker/palette_colors.tsx | 2 + .../apps/lens/group4/color_mapping.ts | 65 +++++++++++++++++++ .../test/functional/apps/lens/group4/index.ts | 1 + .../test/functional/page_objects/lens_page.ts | 35 ++++++++++ 5 files changed, 104 insertions(+) create mode 100644 x-pack/test/functional/apps/lens/group4/color_mapping.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index c96af99e1c792..b61b0e5a3c1c0 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -72,6 +72,7 @@ export const ColorSwatch = ({ aria-label={i18n.translate('coloring.colorMapping.colorPicker.pickAColorAriaLabel', { defaultMessage: 'Pick a color', })} + data-test-subj={`lns-colorMapping-colorSwatch-${index}`} onClick={() => dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} style={{ ...(swatchShape === 'round' ? { borderRadius: '50%', width: 15, height: 15 } : {}), diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx index abaf71a42f35b..c95403b77f80c 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx @@ -58,6 +58,7 @@ export function PaletteColors({ {colors.map((c, index) => ( selectColor({ diff --git a/x-pack/test/functional/apps/lens/group4/color_mapping.ts b/x-pack/test/functional/apps/lens/group4/color_mapping.ts new file mode 100644 index 0000000000000..8f480d11d51af --- /dev/null +++ b/x-pack/test/functional/apps/lens/group4/color_mapping.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; + +import { PASTEL_PALETTE_LIGHT } from '@kbn/coloring/src/shared_components/color_mapping/palettes/pastel'; +import { EUI_PALETTE_COLORS_LIGHT } from '@kbn/coloring/src/shared_components/color_mapping/palettes/eui'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['visualize', 'lens', 'common']); + const elasticChart = getService('elasticChart'); + + describe('lens color mapping', () => { + before(async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + await elasticChart.setNewChartUiDebugFlag(true); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'average', + field: 'bytes', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: 'extension.raw', + palette: { mode: 'colorMapping', id: 'pastel' }, + keepOpen: true, + }); + }); + + it('should render correct color mapping', async () => { + const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); + const legendColors = chart?.legend?.items?.map((item) => item.color.toLowerCase()) ?? []; + expect(legendColors).to.eql(PASTEL_PALETTE_LIGHT.slice(0, 5).map((c) => c.toLowerCase())); + }); + it('should allow switching color mapping palette', async () => { + await PageObjects.lens.changeColorMappingPalette( + 'lnsXY_splitDimensionPanel > lnsLayerPanel-dimensionLink', + 'eui' + ); + const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); + const legendColors = chart?.legend?.items?.map((item) => item.color.toLowerCase()) ?? []; + expect(legendColors).to.eql(EUI_PALETTE_COLORS_LIGHT.slice(0, 5).map((c) => c.toLowerCase())); + }); + + it('should change categorical color', async () => { + await PageObjects.lens.changeColorMappingCategoricalColors( + 'lnsXY_splitDimensionPanel > lnsLayerPanel-dimensionLink', + 0, + 3 + ); + const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); + const firstLegendItemColor = chart?.legend?.items?.[0]?.color?.toLowerCase() ?? 'NONE'; + expect(firstLegendItemColor).to.eql(EUI_PALETTE_COLORS_LIGHT[3].toLowerCase()); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group4/index.ts b/x-pack/test/functional/apps/lens/group4/index.ts index 13cfa3fe421e1..9627b744300ba 100644 --- a/x-pack/test/functional/apps/lens/group4/index.ts +++ b/x-pack/test/functional/apps/lens/group4/index.ts @@ -73,6 +73,7 @@ export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext // total run time ~16m 30s loadTestFile(require.resolve('./colors')); // 1m 2s + loadTestFile(require.resolve('./color_mapping')); loadTestFile(require.resolve('./chart_data')); // 1m 10s loadTestFile(require.resolve('./time_shift')); // 1m loadTestFile(require.resolve('./dashboard')); // 6m 45s diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 9d8a9a7ecec7f..46c7ed27cd349 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1875,5 +1875,40 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await testSubjects.click('lensSuggestionsPanelToggleButton'); } }, + + async changeColorMappingPalette(selector: string, paletteId: string) { + await retry.try(async () => { + if (!(await testSubjects.exists('lns-indexPattern-dimensionContainerClose'))) { + await testSubjects.click(selector); + } + await testSubjects.existOrFail('lns-indexPattern-dimensionContainerClose'); + }); + await this.setPalette(paletteId, false); + await this.closeDimensionEditor(); + }, + + async changeColorMappingCategoricalColors( + selector: string, + colorSwatchIndex: number, + paletteColorIndex: number + ) { + await retry.try(async () => { + if (!(await testSubjects.exists('lns-indexPattern-dimensionContainerClose'))) { + await testSubjects.click(selector); + } + await testSubjects.existOrFail('lns-indexPattern-dimensionContainerClose'); + }); + await testSubjects.click('lns_colorEditing_trigger'); + // disable autoAssign + await testSubjects.setEuiSwitch('lns-colorMapping-autoAssignSwitch', 'uncheck'); + + await testSubjects.click(`lns-colorMapping-colorSwatch-${colorSwatchIndex}`); + + await testSubjects.click(`lns-colorMapping-colorPicker-staticColor-${paletteColorIndex}`); + + await this.closePaletteEditor(); + + await this.closeDimensionEditor(); + }, }); } From 5e36f8cc36c4722efc34e131c5ed949cc8fa2c94 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:15:29 +0000 Subject: [PATCH 36/71] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/test/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 558660c435b8a..93c14653f6189 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -141,5 +141,6 @@ "@kbn/aiops-utils", "@kbn/stack-alerts-plugin", "@kbn/apm-data-access-plugin", + "@kbn/coloring", ] } From ddd216457d66912de43c7688ae99044c2c34bbd2 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 11 Sep 2023 13:23:03 +0200 Subject: [PATCH 37/71] increase bundle limit --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 2344cf1b5277e..e9358719e1ae4 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -49,7 +49,7 @@ pageLoadAssetSize: expressionLegacyMetricVis: 23121 expressionMetric: 22238 expressionMetricVis: 23121 - expressionPartitionVis: 27000 + expressionPartitionVis: 28000 expressionRepeatImage: 22341 expressionRevealImage: 25675 expressions: 140958 From 1f8e9f7c0b7fbfe396651f9da069a4a5e8c4d06f Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 12 Sep 2023 14:17:37 +0200 Subject: [PATCH 38/71] fix single category gradient color --- .../color_mapping/color/color_handling.ts | 8 ++++---- .../components/assignment/special_assignment.tsx | 4 +++- .../color_mapping/components/container/container.tsx | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index 5f29bacd25f2e..f419b853c62af 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -45,7 +45,7 @@ export function getAssignmentColor( : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); const colorScale = scaleSequential(piecewise(interpolateLab, steps)); - return colorScale(index / total); + return total === 0 ? 'red' : total === 1 ? colorScale(0) : colorScale(index / (total - 1)); } } } @@ -111,7 +111,7 @@ export function getColorFactory( getPaletteFn, isDarkMode, autoAssignmentIndex, - assignments.length - 1 + assignments.length ); } // if no auto-assign color rule/color is available then use the other color @@ -133,7 +133,7 @@ export function getColorFactory( getPaletteFn, isDarkMode, matchingAssignmentIndex, - assignments.length - 1 + assignments.length ); } // if no assign color rule/color is available then use the other color @@ -152,7 +152,7 @@ export function getColorFactory( getPaletteFn, isDarkMode, matchingAssignmentIndex, - assignments.length - 1 + assignments.length ); } return getColor(model.specialAssignments[0].color, getPaletteFn, isDarkMode); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx index e5bd3afc2857c..ccba816a51199 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -21,12 +21,14 @@ export function SpecialAssignment({ palette, getPaletteFn, isDarkMode, + total, }: { isDarkMode: boolean; index: number; assignment: ColorMapping.Config['specialAssignments'][number]; palette: ColorMapping.CategoricalPalette; getPaletteFn: ReturnType; + total: number; }) { const dispatch = useDispatch(); const canPickColor = true; @@ -41,7 +43,7 @@ export function SpecialAssignment({ getPaletteFn={getPaletteFn} index={index} palette={palette} - total={index} + total={total} swatchShape="square" isDarkMode={isDarkMode} onColorChange={(color) => { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index 5f0b96891cc78..ee2062d8a4a50 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -128,7 +128,7 @@ export function Container(props: { key={i} data={props.data} index={i} - total={assignments.length - 1} + total={assignments.length} colorMode={colorMode} canPickColor={!autoAssignmentMode && colorMode.type !== 'gradient'} palette={palette} @@ -156,6 +156,7 @@ export function Container(props: { isDarkMode={props.isDarkMode} getPaletteFn={getPaletteFn} assignment={assignment} + total={specialAssignments.length} /> ); })} From 04647eeb9baced50bbe4fda1ad7ea50c705cf7ee Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 12 Sep 2023 17:07:02 +0200 Subject: [PATCH 39/71] use chroma scale instead of d3 --- .../color_mapping/color/color_handling.ts | 15 ++++++++++----- .../components/palette_selector/gradient.tsx | 9 +++++---- .../expression_xy/public/components/xy_chart.tsx | 2 ++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index f419b853c62af..e3f933e27118a 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -5,9 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import { scaleSequential } from 'd3-scale'; -import { interpolateLab, piecewise } from 'd3-interpolate'; +import chroma from 'chroma-js'; +// import { scaleSequential } from 'd3-scale'; +// import { interpolateLab, piecewise } from 'd3-interpolate'; import { ColorMapping } from '../config'; import { changeAlpha, combineColors } from './color_math'; import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; @@ -44,8 +44,13 @@ export function getAssignmentColor( ] : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); - const colorScale = scaleSequential(piecewise(interpolateLab, steps)); - return total === 0 ? 'red' : total === 1 ? colorScale(0) : colorScale(index / (total - 1)); + // const colorScale = scaleSequential(piecewise(interp`olateLab, steps)); + const colorScale = chroma.scale(steps).mode('lab'); + return total === 0 + ? 'red' + : total === 1 + ? colorScale(0).hex() + : colorScale(index / (total - 1)).hex(); } } } diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx index d204bfa998f40..65a20eea14e23 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -5,11 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import chroma from 'chroma-js'; + import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { useDispatch } from 'react-redux'; -import { scaleSequential } from 'd3-scale'; -import { interpolateLab, piecewise } from 'd3-interpolate'; + import { changeAlpha, getValidColor, combineColors } from '../../color/color_math'; import { ColorMapping } from '../../config'; @@ -47,12 +48,12 @@ export function Gradient({ : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)) : []; - const gradientColorScale = scaleSequential(piecewise(interpolateLab, gradientColorSteps)); + const gradientColorScale = chroma.scale(gradientColorSteps).mode('lab'); const gradientCSSBackground = colorMode.type === 'gradient' ? colorMode.steps.length === 1 ? gradientColorSteps.join(',') - : Array.from({ length: 10 }, (d, i) => gradientColorScale(i / 10)) + : Array.from({ length: 10 }, (d, i) => gradientColorScale(i / 10).hex()) .sort(() => (colorMode.sort === 'asc' ? -1 : 1)) .join(',') : ''; diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index c241e476db5de..b361b52055cd4 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -281,6 +281,7 @@ export function XYChart({ const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { datatables: cursorSyncLayers.map(({ table }) => table), }); + console.log(data); const onRenderChange = useCallback( (isRendered: boolean = true) => { @@ -390,6 +391,7 @@ export function XYChart({ timeZone, xAxisConfig?.extent ); + console.log(dataLayers); const axisTitlesVisibilitySettings = { yLeft: yAxesMap?.left?.showTitle ?? true, From c12382363174cbaeb5a85b10649854b4185f625f Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 13 Sep 2023 10:01:03 +0200 Subject: [PATCH 40/71] update tests after changing d3 scale to chroma --- .../color_mapping/color/color_handling.test.ts | 2 +- .../expression_xy/public/components/xy_chart.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts index 0a89360d3e0c7..4e04b4b58e727 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts @@ -223,7 +223,7 @@ describe('Color mapping - color generation', () => { } ); expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); // EUI green - expect(toHex(colorFactory('cat2'))).toBe('#a3908f'); // red gray green + expect(toHex(colorFactory('cat2'))).toBe('#a4908f'); // red gray green expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[2])); // EUI pink }); diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index b361b52055cd4..c241e476db5de 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -281,7 +281,6 @@ export function XYChart({ const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { datatables: cursorSyncLayers.map(({ table }) => table), }); - console.log(data); const onRenderChange = useCallback( (isRendered: boolean = true) => { @@ -391,7 +390,6 @@ export function XYChart({ timeZone, xAxisConfig?.extent ); - console.log(dataLayers); const axisTitlesVisibilitySettings = { yLeft: yAxesMap?.left?.showTitle ?? true, From 2e876ab5158c88a152f105d38d7944d121fdd8a2 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 13 Sep 2023 15:14:26 +0200 Subject: [PATCH 41/71] fix allow categories with whitespaces --- .../src/shared_components/color_mapping/README.md | 2 +- .../color_mapping/categorical_color_mapping.test.tsx | 7 +++++-- .../color_mapping/color/color_handling.test.ts | 5 +++-- .../color_mapping/components/assignment/match.tsx | 8 ++------ .../public/helpers/color/color_mapping_accessor.ts | 11 +++++------ 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/README.md b/packages/kbn-coloring/src/shared_components/color_mapping/README.md index c9e563fb6dc30..6ac41521fe6d4 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/README.md +++ b/packages/kbn-coloring/src/shared_components/color_mapping/README.md @@ -60,7 +60,7 @@ function getColorFactory( where given the model, a palette getter, the theme mode (dark/light) and a list of categories, it will return a function that can be used to pick the right color based on the passed category. -A `category` can be in the shape of a plain string (trimmed) or an array of strings. Numbers, MultiFieldKey, IP etc needs to be stringified. +A `category` can be in the shape of a plain string or an array of strings. Numbers, MultiFieldKey, IP etc needs to be stringified. The `CategoricalColorMapping` React component has the following props: diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx index a117ebe1846cc..2165139125da9 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx @@ -76,10 +76,10 @@ describe('color mapping', () => { }); }); - it('handle special tokens and multi-fields keys', () => { + it('handle special tokens, multi-fields keys and non-trimmed whitespaces', () => { const dataInput: ColorMappingInputData = { type: 'categories', - categories: ['__other__', ['fieldA', 'fieldB'], '__empty__'], + categories: ['__other__', ['fieldA', 'fieldB'], '__empty__', ' with-whitespaces '], }; const onModelUpdateFn = jest.fn(); const component = mount( @@ -108,5 +108,8 @@ describe('color mapping', () => { const assignment3 = component.find(ASSIGNMENT_ITEM(2)).hostNodes(); expect(assignment3.text()).toEqual('(Empty)'); + + const assignment4 = component.find(ASSIGNMENT_ITEM(3)).hostNodes(); + expect(assignment4.text()).toEqual(' with-whitespaces '); }); }); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts index 4e04b4b58e727..06b4a9711428e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts @@ -82,14 +82,15 @@ describe('Color mapping - color generation', () => { expect(colorFactory('not_available')).toBe(NEUTRAL_COLOR_DARK[DEFAULT_NEUTRAL_PALETTE_INDEX]); }); - it('handles special tokens and multi-field categories', () => { + it('handles special tokens, multi-field categories and non-trimmed whitespaces', () => { const colorFactory = getColorFactory(DEFAULT_COLOR_MAPPING_CONFIG, getPaletteFn, false, { type: 'categories', - categories: ['__other__', ['fieldA', 'fieldB'], '__empty__'], + categories: ['__other__', ['fieldA', 'fieldB'], '__empty__', ' with-whitespaces '], }); expect(colorFactory('__other__')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); expect(colorFactory(['fieldA', 'fieldB'])).toBe(EUI_PALETTE_COLORS_LIGHT[1]); expect(colorFactory('__empty__')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); + expect(colorFactory(' with-whitespaces ')).toBe(EUI_PALETTE_COLORS_LIGHT[3]); }); it('ignores configured assignments in auto mode', () => { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index 727c077720482..dda0b267beed3 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -76,12 +76,8 @@ export const Match: React.FC<{ }, []) ); }} - onCreateOption={(e) => { - const label = e.trim(); - if ( - selectedOptions.findIndex((option) => option.label.trim().toLowerCase() === label) === - -1 - ) { + onCreateOption={(label) => { + if (selectedOptions.findIndex((option) => option.label.toLowerCase() === label) === -1) { updateValue([...selectedOptions, { label, value: label }].map((d) => d.value)); } }} diff --git a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts index 80c5f53bbce02..b57f371eab2fd 100644 --- a/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts +++ b/src/plugins/chart_expressions/expression_xy/public/helpers/color/color_mapping_accessor.ts @@ -35,13 +35,12 @@ export function getColorSeriesAccessorFn( if (splitValue === undefined) { return null; } - // category can be also a number, range, ip, multi-field. We need to stringify it to - // be sure we - const category = `${splitValue}`; + + // category can be also a number, range, ip, multi-field. We need to stringify it to be sure + // we can correctly match it a with user string // if the separator exist, we de-construct it into a multifieldkey into values. - const categories = category.split(MULTI_FIELD_KEY_SEPARATOR).map((c) => { - const trimmedCategory = c.trim(); - return specialHandlingInverseMap.get(trimmedCategory) ?? trimmedCategory; + const categories = `${splitValue}`.split(MULTI_FIELD_KEY_SEPARATOR).map((category) => { + return specialHandlingInverseMap.get(category) ?? category; }); // we must keep the array nature of a multi-field key or just use a single string // This is required because the rule stored are checked differently for single values or multi-values From 0548fc7e64caa0aca9792c779338c22f6a9ad639 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 13 Sep 2023 15:15:35 +0200 Subject: [PATCH 42/71] renamed multi-field-value-separator const --- .../color_mapping/categorical_color_mapping.test.tsx | 4 ++-- .../color_mapping/components/assignment/match.tsx | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx index 2165139125da9..8f5e41c95c333 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx @@ -11,7 +11,7 @@ import { mount } from 'enzyme'; import { CategoricalColorMapping, ColorMappingInputData } from './categorical_color_mapping'; import { AVAILABLE_PALETTES } from './palettes/available_palettes'; import { DEFAULT_COLOR_MAPPING_CONFIG } from './config/default_color_mapping'; -import { MULTI_FIELD_VALUES_SEPARATOR } from './components/assignment/match'; +import { MULTI_FIELD_KEY_SEPARATOR } from './components/assignment/match'; const AUTO_ASSIGN_SWITCH = '[data-test-subj="lns-colorMapping-autoAssignSwitch"]'; const ASSIGNMENTS_LIST = '[data-test-subj="lns-colorMapping-assignmentsList"]'; @@ -104,7 +104,7 @@ describe('color mapping', () => { expect(assignment1.text()).toEqual('Other'); const assignment2 = component.find(ASSIGNMENT_ITEM(1)).hostNodes(); - expect(assignment2.text()).toEqual(`fieldA${MULTI_FIELD_VALUES_SEPARATOR}fieldB`); + expect(assignment2.text()).toEqual(`fieldA${MULTI_FIELD_KEY_SEPARATOR}fieldB`); const assignment3 = component.find(ASSIGNMENT_ITEM(2)).hostNodes(); expect(assignment3.text()).toEqual('(Empty)'); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index dda0b267beed3..a813770193df3 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -11,7 +11,7 @@ import { EuiComboBox, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; // TODO: move this outside or configurable -export const MULTI_FIELD_VALUES_SEPARATOR = ' › '; +export const MULTI_FIELD_KEY_SEPARATOR = ' › '; export const Match: React.FC<{ index: number; @@ -33,9 +33,7 @@ export const Match: React.FC<{ : rule.values.map((value) => { const ruleValues = Array.isArray(value) ? value : [value]; return { - label: ruleValues - .map((v) => specialTokens.get(v) ?? v) - .join(MULTI_FIELD_VALUES_SEPARATOR), + label: ruleValues.map((v) => specialTokens.get(v) ?? v).join(MULTI_FIELD_KEY_SEPARATOR), value, }; }); @@ -43,7 +41,7 @@ export const Match: React.FC<{ const convertedOptions = options.map((value) => { const ruleValues = Array.isArray(value) ? value : [value]; return { - label: ruleValues.map((v) => specialTokens.get(v) ?? v).join(MULTI_FIELD_VALUES_SEPARATOR), + label: ruleValues.map((v) => specialTokens.get(v) ?? v).join(MULTI_FIELD_KEY_SEPARATOR), value, }; }); From 04ec3f1702892319b0b410924cf5abffd6d1f18b Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 13 Sep 2023 17:01:11 +0200 Subject: [PATCH 43/71] import MULTI_FIELD_KEY_SEPARATOR from data plugin --- .../color_mapping/categorical_color_mapping.test.tsx | 2 +- .../color_mapping/components/assignment/match.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx index 8f5e41c95c333..3029d2306d2e7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx @@ -11,7 +11,7 @@ import { mount } from 'enzyme'; import { CategoricalColorMapping, ColorMappingInputData } from './categorical_color_mapping'; import { AVAILABLE_PALETTES } from './palettes/available_palettes'; import { DEFAULT_COLOR_MAPPING_CONFIG } from './config/default_color_mapping'; -import { MULTI_FIELD_KEY_SEPARATOR } from './components/assignment/match'; +import { MULTI_FIELD_KEY_SEPARATOR } from '@kbn/data-plugin/common'; const AUTO_ASSIGN_SWITCH = '[data-test-subj="lns-colorMapping-autoAssignSwitch"]'; const ASSIGNMENTS_LIST = '[data-test-subj="lns-colorMapping-assignmentsList"]'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index a813770193df3..b04c2c40680e7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -9,9 +9,8 @@ import React from 'react'; import { EuiComboBox, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { MULTI_FIELD_KEY_SEPARATOR } from '@kbn/data-plugin/common'; import { ColorMapping } from '../../config'; -// TODO: move this outside or configurable -export const MULTI_FIELD_KEY_SEPARATOR = ' › '; export const Match: React.FC<{ index: number; From f54bad2e991ea8ab64d8fede5a6157344b9ad156 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:10:27 +0000 Subject: [PATCH 44/71] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- packages/kbn-coloring/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-coloring/tsconfig.json b/packages/kbn-coloring/tsconfig.json index 4ad624b0780db..370e6d8ec6c5a 100644 --- a/packages/kbn-coloring/tsconfig.json +++ b/packages/kbn-coloring/tsconfig.json @@ -20,6 +20,7 @@ "@kbn/utility-types", "@kbn/shared-ux-utility", "@kbn/test-jest-helpers", + "@kbn/data-plugin", ], "exclude": [ "target/**/*", From a7eddf8513b5905e9259214b45ed78ff8ba5ec67 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 13 Sep 2023 18:14:01 +0200 Subject: [PATCH 45/71] fix dependency on kbn-coloring --- src/plugins/charts/kibana.jsonc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/charts/kibana.jsonc b/src/plugins/charts/kibana.jsonc index 6b0e952969329..8c00cd40f4ad3 100644 --- a/src/plugins/charts/kibana.jsonc +++ b/src/plugins/charts/kibana.jsonc @@ -7,7 +7,8 @@ "server": true, "browser": true, "requiredPlugins": [ - "expressions" + "expressions", + "data" ], "extraPublicDirs": [ "common" From 738e56a4af355a66e20163adf115097caec04f69 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 14 Sep 2023 11:44:35 +0200 Subject: [PATCH 46/71] Code cleanup --- .../color_mapping/categorical_color_mapping.tsx | 7 +++++++ .../color_mapping/color/color_handling.ts | 6 +----- .../components/palette_selector/gradient.tsx | 11 +++++------ .../src/shared_components/color_mapping/index.ts | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx index ca99e08d248ae..be731672dbc38 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx @@ -15,6 +15,10 @@ import { Container } from './components/container/container'; import { ColorMapping } from './config'; import { uiReducer } from './state/ui'; +/** + * A configuration object that is required to populate correctly the visible categories + * or the ranges in the CategoricalColorMapping component + */ export type ColorMappingInputData = | { type: 'categories'; @@ -28,6 +32,9 @@ export type ColorMappingInputData = bins: number; }; +/** + * The props of the CategoricalColorMapping component + */ export interface ColorMappingProps { /** the initial color mapping model, usually coming from a the visualization saved object */ model: ColorMapping.Config; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index e3f933e27118a..200bdc72a4cab 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ import chroma from 'chroma-js'; -// import { scaleSequential } from 'd3-scale'; -// import { interpolateLab, piecewise } from 'd3-interpolate'; import { ColorMapping } from '../config'; import { changeAlpha, combineColors } from './color_math'; import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; @@ -15,8 +13,6 @@ import { getPalette } from '../palette'; import { ColorMappingInputData } from '../categorical_color_mapping'; import { ruleMatch } from './rule_matching'; -export type Color = string; - export function getAssignmentColor( colorMode: ColorMapping.Config['colorMode'], color: ColorMapping.Config['assignments'][number]['color'], @@ -73,7 +69,7 @@ export function getColorFactory( getPaletteFn: ReturnType, isDarkMode: boolean, data: ColorMappingInputData -): (category: string | string[]) => Color { +): (category: string | string[]) => string { const palette = getPaletteFn(model.paletteId); // generate on-the-fly assignments in auto-mode based on current data. // This simplify the code by always using assignments, even if there is no real static assigmnets diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx index 65a20eea14e23..7ac5f921a9698 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -20,7 +20,6 @@ import { getPalette } from '../../palette'; import './gradient.scss'; import { addGradientColorStep, updateGradientColorStep } from '../../state/color_mapping'; -import { ColorCode, CategoricalColor, CategoricalPalette } from '../../config/types'; import { colorPickerVisibility } from '../../state/ui'; export function Gradient({ @@ -172,9 +171,9 @@ function AddStop({ }: { colorMode: { type: 'gradient'; - steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; + steps: Array<(ColorMapping.CategoricalColor | ColorMapping.ColorCode) & { touched: boolean }>; }; - currentPalette: CategoricalPalette; + currentPalette: ColorMapping.CategoricalPalette; at: number; }) { const dispatch = useDispatch(); @@ -219,12 +218,12 @@ function ColorStop({ }: { colorMode: { type: 'gradient'; - steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; + steps: Array<(ColorMapping.CategoricalColor | ColorMapping.ColorCode) & { touched: boolean }>; sort: 'asc' | 'desc'; }; - step: CategoricalColor | ColorCode; + step: ColorMapping.CategoricalColor | ColorMapping.ColorCode; index: number; - currentPalette: CategoricalPalette; + currentPalette: ColorMapping.CategoricalPalette; getPaletteFn: ReturnType; isDarkMode: boolean; }) { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts index bf996cbc80bae..32c580f691215 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export { CategoricalColorMapping } from './categorical_color_mapping'; +export { CategoricalColorMapping, type ColorMappingProps } from './categorical_color_mapping'; export type { ColorMappingInputData } from './categorical_color_mapping'; export type { ColorMapping } from './config'; export * from './palettes/available_palettes'; From b7a7b6b653eb817cdb026aa81941b99cde356e75 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 14 Sep 2023 11:48:20 +0200 Subject: [PATCH 47/71] make gradient input editable --- .../color_mapping/components/assignment/assignment.tsx | 6 ++++-- .../color_mapping/components/container/container.tsx | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index 125caf9352e09..f53f0fe006852 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -30,6 +30,7 @@ export function Assignment({ index, total, canPickColor, + editable, palette, colorMode, getPaletteFn, @@ -45,6 +46,7 @@ export function Assignment({ palette: ColorMapping.CategoricalPalette; getPaletteFn: ReturnType; canPickColor: boolean; + editable: boolean; isDarkMode: boolean; specialTokens: Map; }) { @@ -74,7 +76,7 @@ export function Assignment({ assignment.rule.type === 'matchExactly' || assignment.rule.type === 'matchExactlyCI' ? ( { const rule: ColorMapping.RuleRange = { type: 'range', diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index ee2062d8a4a50..2327667f3f444 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -130,6 +130,7 @@ export function Container(props: { index={i} total={assignments.length} colorMode={colorMode} + editable={!autoAssignmentMode} canPickColor={!autoAssignmentMode && colorMode.type !== 'gradient'} palette={palette} isDarkMode={props.isDarkMode} From 229a50b17cfc58aa0b2280aad84c15fdb0851404 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 14 Sep 2023 16:11:28 +0200 Subject: [PATCH 48/71] fix type checks --- .../public/utils/layers/get_layers.ts | 3 +-- .../public/components/tagcloud_component.tsx | 3 +-- .../plugins/lens/public/visualizations/xy/visualization.tsx | 5 +---- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index c6100b7d64151..af141be2e4186 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -20,7 +20,6 @@ import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { Datatable, DatatableRow } from '@kbn/expressions-plugin/public'; import { getColorCategories } from '@kbn/chart-expressions-common'; -import { Color } from '@kbn/coloring/src/shared_components/color_mapping/color/color_handling'; import { getDistinctSeries } from '..'; import { BucketColumns, ChartTypes, PartitionVisParams } from '../../../common/types'; import { sortPredicateByType, sortPredicateSaveSourceOrder } from './sort_predicate'; @@ -131,7 +130,7 @@ function getColorFromMappingFactory( rows: DatatableRow[], isDarkMode: boolean, colorMapping?: string -): undefined | ((category: string | string[]) => Color) { +): undefined | ((category: string | string[]) => string) { if (!colorMapping) { // return undefined, we will use the legacy color mapping instead return undefined; diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index 719555ced2621..e3532bb17f97e 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -26,7 +26,6 @@ import { getColorCategories, getOverridesFor } from '@kbn/chart-expressions-comm import type { AllowedSettingsOverrides, AllowedChartOverrides } from '@kbn/charts-plugin/common'; import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; import { isMultiFieldKey } from '@kbn/data-plugin/common'; -import { Color } from '@kbn/coloring/src/shared_components/color_mapping/color/color_handling'; import { getFormatService } from '../format_service'; import { TagcloudRendererConfig } from '../../common/types'; import { ScaleOptions, Orientation } from '../../common/constants'; @@ -313,7 +312,7 @@ function getColorFromMappingFactory( rows: DatatableRow[], isDarkMode: boolean, colorMapping?: string -): undefined | ((category: string | string[]) => Color) { +): undefined | ((category: string | string[]) => string) { if (!colorMapping) { // return undefined, we will use the legacy color mapping instead return undefined; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index 60b95799b6939..d76260251757d 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -25,10 +25,7 @@ import type { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; import { isEqual } from 'lodash'; import { type AccessorConfig, DimensionTrigger } from '@kbn/visualization-ui-components'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { - DEFAULT_COLOR_MAPPING_CONFIG, - getPaletteColors, -} from '@kbn/coloring/src/shared_components/color_mapping/config/default_color_mapping'; +import { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors } from '@kbn/coloring'; import useObservable from 'react-use/lib/useObservable'; import { generateId } from '../../id_generator'; import { From 6ebd87729e77e7d41754caf7012bd53b0c0da1d2 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 18 Sep 2023 10:54:02 +0200 Subject: [PATCH 49/71] fix multi-metric pie charts --- .../public/utils/layers/get_layers.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index af141be2e4186..f1a279ba17c4c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -77,7 +77,7 @@ export const getLayers = ( columns, rows, isDarkMode, - visParams.colorMapping + visParams ); return columns.map((col, layerIndex) => { @@ -129,12 +129,23 @@ function getColorFromMappingFactory( columns: Array>, rows: DatatableRow[], isDarkMode: boolean, - colorMapping?: string + visParams: PartitionVisParams ): undefined | ((category: string | string[]) => string) { + const { colorMapping, dimensions } = visParams; + if (!colorMapping) { // return undefined, we will use the legacy color mapping instead return undefined; } + // if pie/donut/treemap multimetric or has no buckets use the default color mode + if ( + (chartType === ChartTypes.DONUT || + chartType === ChartTypes.PIE || + chartType === ChartTypes.TREEMAP) && + (!dimensions.buckets || dimensions.buckets?.length === 0 || dimensions.metrics.length > 1) + ) { + return undefined; + } // the mosaic configures the main categories in the second column, instead of the first // as it happens in all the other partition types. // Independentely from the bucket aggregation used, the categories will always be casted From 385fca482d2d3f8c53443f5f9c60d8eb0747c688 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 18 Sep 2023 11:07:21 +0200 Subject: [PATCH 50/71] finish the readme description --- .../shared_components/color_mapping/README.md | 17 ++++++++++------- .../color_mapping/categorical_color_mapping.tsx | 8 +++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/README.md b/packages/kbn-coloring/src/shared_components/color_mapping/README.md index 6ac41521fe6d4..220824ca47820 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/README.md +++ b/packages/kbn-coloring/src/shared_components/color_mapping/README.md @@ -5,10 +5,10 @@ This shared component can be used to define a color mapping as an association of This package provides: - a React component, called `CategoricalColorMapping` that provides a simplified UI (that in general can be hosted in a flyout), that helps the user generate a `ColorMapping.Config` object that descibes the mappings configuration - a function `getColorFactory` that given a color mapping configuration returns a function that maps a passed category to the corresponding color -- a definition scheme for the color mapping, based on the type `ColorMapping.Config`, that provides an extensible way of describing the link betwe +- a definition scheme for the color mapping, based on the type `ColorMapping.Config`, that provides an extensible way of describing the link between colors and rules. Collects the minimal information required apply colors based on categories. Together with the `ColorMappingInputData` can be used to get colors in a deterministic way. -An example of a configuration is the following: +An example of the configuration is the following: ```ts const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { assignmentMode: 'auto', @@ -45,7 +45,8 @@ const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { }; ``` -The function `getColorFactory` has the following type: +The function `getColorFactory` is a curry function where, given the model, a palette getter, the theme mode (dark/light) and a list of categories, returns a function that can be used to pick the right color based on a given category. + ```ts function getColorFactory( model: ColorMapping.Config, @@ -57,7 +58,7 @@ function getColorFactory( } ): (category: string | string[]) => Color ``` -where given the model, a palette getter, the theme mode (dark/light) and a list of categories, it will return a function that can be used to pick the right color based on the passed category. + A `category` can be in the shape of a plain string or an array of strings. Numbers, MultiFieldKey, IP etc needs to be stringified. @@ -67,15 +68,17 @@ The `CategoricalColorMapping` React component has the following props: ```tsx function CategoricalColorMapping(props: { - /** the initial color mapping model, usually coming from a the visualization saved object */ + /** The initial color mapping model, usually coming from a the visualization saved object */ model: ColorMapping.Config; /** A map of paletteId and palette configuration */ palettes: Map; + /** A data description of what needs to be colored */ data: ColorMappingInputData; + /** Theme dark mode */ isDarkMode: boolean; - /** map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ + /** A map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ specialTokens: Map; - /** a function called at every change in the model */ + /** A function called at every change in the model */ onModelUpdate: (model: ColorMapping.Config) => void; }) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx index be731672dbc38..290c549684f90 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.tsx @@ -36,15 +36,17 @@ export type ColorMappingInputData = * The props of the CategoricalColorMapping component */ export interface ColorMappingProps { - /** the initial color mapping model, usually coming from a the visualization saved object */ + /** The initial color mapping model, usually coming from a the visualization saved object */ model: ColorMapping.Config; /** A map of paletteId and palette configuration */ palettes: Map; + /** A data description of what needs to be colored */ data: ColorMappingInputData; + /** Theme dark mode */ isDarkMode: boolean; - /** map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ + /** A map between original and formatted tokens used to handle special cases, like the Other bucket and the empty bucket */ specialTokens: Map; - /** a function called at every change in the model */ + /** A function called at every change in the model */ onModelUpdate: (model: ColorMapping.Config) => void; } From 0e7f29d3cdbed05ee570473ebdd17f3ac72f1024 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 18 Sep 2023 11:33:07 +0200 Subject: [PATCH 51/71] update selector naming convention --- .../components/color_picker/color_swatch.tsx | 4 ++-- .../components/container/container.tsx | 20 +++++++++---------- .../color_mapping/state/selectors.ts | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index b61b0e5a3c1c0..ac051581508c0 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -16,7 +16,7 @@ import { ColorMapping } from '../../config'; import { getPalette } from '../../palette'; import { removeGradientColorStep } from '../../state/color_mapping'; -import { getColorPickerVisibilitySelector } from '../../state/selectors'; +import { selectColorPickerVisibility } from '../../state/selectors'; import { colorPickerVisibility, hideColorPickerVisibility } from '../../state/ui'; interface ColorPickerSwatchProps { colorMode: ColorMapping.Config['colorMode']; @@ -46,7 +46,7 @@ export const ColorSwatch = ({ isDarkMode, forType, }: ColorPickerSwatchProps) => { - const colorPickerState = useSelector(getColorPickerVisibilitySelector); + const colorPickerState = useSelector(selectColorPickerVisibility); const dispatch = useDispatch(); const colorPickerVisible = colorPickerState.index === index && diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index 2327667f3f444..2b605ff88f353 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -35,17 +35,17 @@ import { NeutralPalette } from '../../palettes/default_palettes'; import { getUnusedColorForNewAssignment } from '../../config/assignments'; import { getPalette } from '../../palette'; import { - getColorModeSelector, - getPaletteSelector, - getSpecialAssignmentsSelector, - isAutoAssignmentModeSelector, + selectColorMode, + selectPalette, + selectSpecialAssignments, + selectIsAutoAssignmentMode, } from '../../state/selectors'; import { ColorMappingInputData } from '../../categorical_color_mapping'; import { Gradient } from '../palette_selector/gradient'; export const MAX_ASSIGNABLE_COLORS = 10; -function getComputedAssignmentsSelector( +function selectComputedAssignments( data: ColorMappingInputData, palette: ColorMapping.CategoricalPalette, colorMode: ColorMapping.Config['colorMode'] @@ -66,11 +66,11 @@ export function Container(props: { const getPaletteFn = getPalette(props.palettes, NeutralPalette); - const palette = useSelector(getPaletteSelector(getPaletteFn)); - const colorMode = useSelector(getColorModeSelector); - const autoAssignmentMode = useSelector(isAutoAssignmentModeSelector); - const assignments = useSelector(getComputedAssignmentsSelector(props.data, palette, colorMode)); - const specialAssignments = useSelector(getSpecialAssignmentsSelector); + const palette = useSelector(selectPalette(getPaletteFn)); + const colorMode = useSelector(selectColorMode); + const autoAssignmentMode = useSelector(selectIsAutoAssignmentMode); + const assignments = useSelector(selectComputedAssignments(props.data, palette, colorMode)); + const specialAssignments = useSelector(selectSpecialAssignments); const canAddNewAssignment = !autoAssignmentMode && assignments.length < MAX_ASSIGNABLE_COLORS; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts b/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts index 24d5c60a8740d..7e9c852333f43 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts @@ -9,18 +9,18 @@ import { getPalette } from '../palette'; import { RootState } from './color_mapping'; -export function getPaletteSelector(getPaletteFn: ReturnType) { +export function selectPalette(getPaletteFn: ReturnType) { return (state: RootState) => getPaletteFn(state.colorMapping.paletteId); } -export function getColorModeSelector(state: RootState) { +export function selectColorMode(state: RootState) { return state.colorMapping.colorMode; } -export function getSpecialAssignmentsSelector(state: RootState) { +export function selectSpecialAssignments(state: RootState) { return state.colorMapping.specialAssignments; } -export function isAutoAssignmentModeSelector(state: RootState) { +export function selectIsAutoAssignmentMode(state: RootState) { return state.colorMapping.assignmentMode === 'auto'; } -export function getColorPickerVisibilitySelector(state: RootState) { +export function selectColorPickerVisibility(state: RootState) { return state.ui.colorPicker; } From 3ec9a028e23837aa6bb889497a7805c2c55ef7af Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 18 Sep 2023 11:44:43 +0200 Subject: [PATCH 52/71] reposition popover on scroll --- .../color_mapping/components/color_picker/color_swatch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index ac051581508c0..2d649a135392a 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -65,6 +65,7 @@ export const ColorSwatch = ({ dispatch(hideColorPickerVisibility())} button={ Date: Mon, 18 Sep 2023 11:49:10 +0200 Subject: [PATCH 53/71] open color picker popup preferably toward up --- .../color_mapping/components/color_picker/color_swatch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index 2d649a135392a..dc697f40afe65 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -67,6 +67,7 @@ export const ColorSwatch = ({ isOpen={colorPickerVisible} repositionOnScroll={true} closePopover={() => dispatch(hideColorPickerVisibility())} + anchorPosition="upLeft" button={ Date: Tue, 19 Sep 2023 12:14:05 +0200 Subject: [PATCH 54/71] increase bundle limit --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index e9358719e1ae4..b2fdcafde2ca4 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -55,7 +55,7 @@ pageLoadAssetSize: expressions: 140958 expressionShape: 34008 expressionTagcloud: 27505 - expressionXY: 39500 + expressionXY: 45000 features: 21723 fieldFormats: 65209 files: 22673 From 15c2fcc2e71859414a46c2831601abf953d646ce Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 19 Sep 2023 15:25:30 +0200 Subject: [PATCH 55/71] cleanup and update color palettes --- .../categorical_color_mapping.test.tsx | 2 +- .../color/color_handling.test.ts | 95 +++++++++++++------ .../color_mapping/color/color_handling.ts | 2 +- .../components/assignment/assignment.tsx | 2 +- .../assignment/special_assignment.tsx | 2 +- .../components/color_picker/color_picker.tsx | 4 +- .../components/color_picker/color_swatch.tsx | 2 +- .../color_picker/palette_colors.tsx | 4 +- .../components/color_picker/rgb_picker.tsx | 2 +- .../components/container/container.tsx | 4 +- .../components/palette_selector/gradient.tsx | 2 +- .../palette_selector/palette_selector.tsx | 2 +- .../color_mapping/config/assignments.ts | 3 +- .../config/default_color_mapping.ts | 7 +- .../shared_components/color_mapping/index.ts | 3 +- .../palettes/available_palettes.ts | 24 ----- .../palettes/default_palettes.ts | 74 --------------- .../color_mapping/palettes/elastic_brand.ts | 28 ++++++ .../palettes/{eui.ts => eui_amsterdam.ts} | 26 ++--- .../color_mapping/palettes/ikea.ts | 10 -- .../{palette.ts => palettes/index.ts} | 18 +++- .../color_mapping/palettes/kibana_legacy.ts | 29 ++++++ .../color_mapping/palettes/neutral.ts | 12 +++ .../color_mapping/palettes/pastel.ts | 28 ------ .../color_mapping/palettes/tableau.ts | 32 ------- .../color_mapping/state/selectors.ts | 2 +- .../public/utils/layers/get_layers.ts | 2 +- .../apps/lens/group4/color_mapping.ts | 22 +++-- 28 files changed, 201 insertions(+), 242 deletions(-) delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/elastic_brand.ts rename packages/kbn-coloring/src/shared_components/color_mapping/palettes/{eui.ts => eui_amsterdam.ts} (55%) delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts rename packages/kbn-coloring/src/shared_components/color_mapping/{palette.ts => palettes/index.ts} (55%) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/kibana_legacy.ts delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx index 3029d2306d2e7..fe8374d7dcdcd 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/categorical_color_mapping.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { CategoricalColorMapping, ColorMappingInputData } from './categorical_color_mapping'; -import { AVAILABLE_PALETTES } from './palettes/available_palettes'; +import { AVAILABLE_PALETTES } from './palettes'; import { DEFAULT_COLOR_MAPPING_CONFIG } from './config/default_color_mapping'; import { MULTI_FIELD_KEY_SEPARATOR } from '@kbn/data-plugin/common'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts index 06b4a9711428e..2e36547a98821 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts @@ -11,11 +11,12 @@ import { DEFAULT_NEUTRAL_PALETTE_INDEX, } from '../config/default_color_mapping'; import { getColorFactory } from './color_handling'; -import { getPalette } from '../palette'; -import { AVAILABLE_PALETTES } from '../palettes/available_palettes'; -import { EUIPalette, NeutralPalette } from '../palettes/default_palettes'; -import { EUI_PALETTE_COLORS_DARK, EUI_PALETTE_COLORS_LIGHT } from '../palettes/eui'; -import { NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from '../palettes/neutral'; +import { getPalette, AVAILABLE_PALETTES } from '../palettes'; +import { + EUIAmsterdamColorBlindPalette, + EUI_AMSTERDAM_PALETTE_COLORS, +} from '../palettes/eui_amsterdam'; +import { NeutralPalette, NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from '../palettes/neutral'; import { toHex } from './color_math'; import { ColorMapping } from '../config'; @@ -27,9 +28,9 @@ describe('Color mapping - color generation', () => { type: 'categories', categories: ['catA', 'catB', 'catC'], }); - expect(colorFactory('catA')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); - expect(colorFactory('catB')).toBe(EUI_PALETTE_COLORS_LIGHT[1]); - expect(colorFactory('catC')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); + expect(colorFactory('catA')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[0]); + expect(colorFactory('catB')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[1]); + expect(colorFactory('catC')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[2]); // if the category is not available in the `categories` list then a default neutral color is used expect(colorFactory('not_available')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]); }); @@ -75,9 +76,9 @@ describe('Color mapping - color generation', () => { type: 'categories', categories: ['catA', 'catB', 'catC'], }); - expect(colorFactory('catA')).toBe(EUI_PALETTE_COLORS_DARK[0]); - expect(colorFactory('catB')).toBe(EUI_PALETTE_COLORS_DARK[1]); - expect(colorFactory('catC')).toBe(EUI_PALETTE_COLORS_DARK[2]); + expect(colorFactory('catA')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[0]); + expect(colorFactory('catB')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[1]); + expect(colorFactory('catC')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[2]); // if the category is not available in the `categories` list then a default neutral color is used expect(colorFactory('not_available')).toBe(NEUTRAL_COLOR_DARK[DEFAULT_NEUTRAL_PALETTE_INDEX]); }); @@ -87,10 +88,10 @@ describe('Color mapping - color generation', () => { type: 'categories', categories: ['__other__', ['fieldA', 'fieldB'], '__empty__', ' with-whitespaces '], }); - expect(colorFactory('__other__')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); - expect(colorFactory(['fieldA', 'fieldB'])).toBe(EUI_PALETTE_COLORS_LIGHT[1]); - expect(colorFactory('__empty__')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); - expect(colorFactory(' with-whitespaces ')).toBe(EUI_PALETTE_COLORS_LIGHT[3]); + expect(colorFactory('__other__')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[0]); + expect(colorFactory(['fieldA', 'fieldB'])).toBe(EUI_AMSTERDAM_PALETTE_COLORS[1]); + expect(colorFactory('__empty__')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[2]); + expect(colorFactory(' with-whitespaces ')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[3]); }); it('ignores configured assignments in auto mode', () => { @@ -112,9 +113,9 @@ describe('Color mapping - color generation', () => { categories: ['catA', 'catB', 'assignmentToIgnore'], } ); - expect(colorFactory('catA')).toBe(EUI_PALETTE_COLORS_LIGHT[0]); - expect(colorFactory('catB')).toBe(EUI_PALETTE_COLORS_LIGHT[1]); - expect(colorFactory('assignmentToIgnore')).toBe(EUI_PALETTE_COLORS_LIGHT[2]); + expect(colorFactory('catA')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[0]); + expect(colorFactory('catB')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[1]); + expect(colorFactory('assignmentToIgnore')).toBe(EUI_AMSTERDAM_PALETTE_COLORS[2]); }); it('color with auto rule are assigned in order of the configured data input', () => { @@ -163,7 +164,14 @@ describe('Color mapping - color generation', () => { ...DEFAULT_COLOR_MAPPING_CONFIG, colorMode: { type: 'gradient', - steps: [{ type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }], + steps: [ + { + type: 'categorical', + paletteId: EUIAmsterdamColorBlindPalette.id, + colorIndex: 0, + touched: false, + }, + ], sort: 'desc', }, }, @@ -175,7 +183,7 @@ describe('Color mapping - color generation', () => { } ); // this matches exactly with the initial step selected - expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); + expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_AMSTERDAM_PALETTE_COLORS[0])); expect(toHex(colorFactory('cat2'))).toBe('#93cebc'); expect(toHex(colorFactory('cat3'))).toBe('#cce8e0'); }); @@ -186,7 +194,14 @@ describe('Color mapping - color generation', () => { ...DEFAULT_COLOR_MAPPING_CONFIG, colorMode: { type: 'gradient', - steps: [{ type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }], + steps: [ + { + type: 'categorical', + paletteId: EUIAmsterdamColorBlindPalette.id, + colorIndex: 0, + touched: false, + }, + ], sort: 'asc', }, }, @@ -200,7 +215,7 @@ describe('Color mapping - color generation', () => { expect(toHex(colorFactory('cat1'))).toBe('#cce8e0'); expect(toHex(colorFactory('cat2'))).toBe('#93cebc'); // this matches exactly with the initial step selected - expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); + expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_AMSTERDAM_PALETTE_COLORS[0])); }); it('returns 2 colors gradient [desc, lightMode]', () => { @@ -210,8 +225,18 @@ describe('Color mapping - color generation', () => { colorMode: { type: 'gradient', steps: [ - { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }, - { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 2, touched: false }, + { + type: 'categorical', + paletteId: EUIAmsterdamColorBlindPalette.id, + colorIndex: 0, + touched: false, + }, + { + type: 'categorical', + paletteId: EUIAmsterdamColorBlindPalette.id, + colorIndex: 2, + touched: false, + }, ], sort: 'desc', }, @@ -223,9 +248,9 @@ describe('Color mapping - color generation', () => { categories: ['cat1', 'cat2', 'cat3'], } ); - expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); // EUI green + expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_AMSTERDAM_PALETTE_COLORS[0])); // EUI green expect(toHex(colorFactory('cat2'))).toBe('#a4908f'); // red gray green - expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[2])); // EUI pink + expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_AMSTERDAM_PALETTE_COLORS[2])); // EUI pink }); it('returns divergent gradient [asc, darkMode]', () => { @@ -235,9 +260,19 @@ describe('Color mapping - color generation', () => { colorMode: { type: 'gradient', steps: [ - { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 0, touched: false }, + { + type: 'categorical', + paletteId: EUIAmsterdamColorBlindPalette.id, + colorIndex: 0, + touched: false, + }, { type: 'categorical', paletteId: NeutralPalette.id, colorIndex: 0, touched: false }, - { type: 'categorical', paletteId: EUIPalette.id, colorIndex: 2, touched: false }, + { + type: 'categorical', + paletteId: EUIAmsterdamColorBlindPalette.id, + colorIndex: 2, + touched: false, + }, ], sort: 'asc', // testing in ascending order }, @@ -249,9 +284,9 @@ describe('Color mapping - color generation', () => { categories: ['cat1', 'cat2', 'cat3'], } ); - expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_PALETTE_COLORS_DARK[2])); // EUI pink + expect(toHex(colorFactory('cat1'))).toBe(toHex(EUI_AMSTERDAM_PALETTE_COLORS[2])); // EUI pink expect(toHex(colorFactory('cat2'))).toBe(NEUTRAL_COLOR_DARK[0]); // NEUTRAL LIGHT GRAY - expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_PALETTE_COLORS_LIGHT[0])); // EUI green + expect(toHex(colorFactory('cat3'))).toBe(toHex(EUI_AMSTERDAM_PALETTE_COLORS[0])); // EUI green expect(toHex(colorFactory('not available cat'))).toBe( toHex(NEUTRAL_COLOR_DARK[DEFAULT_NEUTRAL_PALETTE_INDEX]) ); // check the other diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index 200bdc72a4cab..b60f1288e7c2f 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -9,7 +9,7 @@ import chroma from 'chroma-js'; import { ColorMapping } from '../config'; import { changeAlpha, combineColors } from './color_math'; import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; -import { getPalette } from '../palette'; +import { getPalette } from '../palettes'; import { ColorMappingInputData } from '../categorical_color_mapping'; import { ruleMatch } from './rule_matching'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index f53f0fe006852..2480d2acc6b90 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -18,7 +18,7 @@ import { import { ColorMapping } from '../../config'; import { Range } from './range'; import { Match } from './match'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; import { ColorMappingInputData } from '../../categorical_color_mapping'; import { ColorSwatch } from '../color_picker/color_swatch'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx index ccba816a51199..272331cfe8eb8 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; import { ColorSwatch } from '../color_picker/color_swatch'; import { updateSpecialAssignmentColor } from '../../state/color_mapping'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx index a97a8df1611f2..f5d3cc557a949 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_picker.tsx @@ -10,10 +10,10 @@ import React, { useState } from 'react'; import { EuiButton, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; -import { NeutralPalette } from '../../palettes/default_palettes'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; import { PaletteColors } from './palette_colors'; import { RGBPicker } from './rgb_picker'; +import { NeutralPalette } from '../../palettes/neutral'; export function ColorPicker({ palette, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index dc697f40afe65..c99738542ee02 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { ColorPicker } from './color_picker'; import { getAssignmentColor } from '../../color/color_handling'; import { ColorMapping } from '../../config'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; import { removeGradientColorStep } from '../../state/color_mapping'; import { selectColorPickerVisibility } from '../../state/selectors'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx index c95403b77f80c..5e1531f134313 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/palette_colors.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { EuiColorPickerSwatch, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; -import { NeutralPalette } from '../../palettes/default_palettes'; +import { getPalette } from '../../palettes'; import { isSameColor } from '../../color/color_math'; -import { getPalette } from '../../palette'; +import { NeutralPalette } from '../../palettes/neutral'; export function PaletteColors({ palette, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx index 494eec3049e91..7f0e26bf7b82b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { ColorMapping } from '../../config'; import { getColorContrast } from '../../color/color_math'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; export function RGBPicker({ isDarkMode, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index 2b605ff88f353..eabd17ea3c844 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -31,9 +31,8 @@ import { } from '../../state/color_mapping'; import { generateAutoAssignmentsForCategories } from '../../config/assignment_from_categories'; import { ColorMapping } from '../../config'; -import { NeutralPalette } from '../../palettes/default_palettes'; +import { getPalette } from '../../palettes'; import { getUnusedColorForNewAssignment } from '../../config/assignments'; -import { getPalette } from '../../palette'; import { selectColorMode, selectPalette, @@ -42,6 +41,7 @@ import { } from '../../state/selectors'; import { ColorMappingInputData } from '../../categorical_color_mapping'; import { Gradient } from '../palette_selector/gradient'; +import { NeutralPalette } from '../../palettes/neutral'; export const MAX_ASSIGNABLE_COLORS = 10; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx index 7ac5f921a9698..9bc656d1d197b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -15,7 +15,7 @@ import { changeAlpha, getValidColor, combineColors } from '../../color/color_mat import { ColorMapping } from '../../config'; import { ColorSwatch } from '../color_picker/color_swatch'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; import './gradient.scss'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index d93ed985abea6..c37b79043e75e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { RootState, updatePalette } from '../../state/color_mapping'; import { ColorMapping } from '../../config'; import { updateAssignmentsPalette, updateColorModePalette } from '../../config/assignments'; -import { getPalette } from '../../palette'; +import { getPalette } from '../../palettes'; export function PaletteSelector({ palettes, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts index eac63d578e47f..701baa1b1710b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/assignments.ts @@ -8,8 +8,7 @@ import type { ColorMapping } from '.'; import { MAX_ASSIGNABLE_COLORS } from '../components/container/container'; -import { NeutralPalette } from '../palettes/default_palettes'; -import { getPalette } from '../palette'; +import { getPalette, NeutralPalette } from '../palettes'; import { DEFAULT_NEUTRAL_PALETTE_INDEX } from './default_color_mapping'; export function updateAssignmentsPalette( diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index af59b15032810..b377d92fbd33e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -7,8 +7,9 @@ */ import { ColorMapping } from '.'; -import { AVAILABLE_PALETTES } from '../palettes/available_palettes'; -import { NeutralPalette, getPalette, EUIPalette } from '../palettes/default_palettes'; +import { AVAILABLE_PALETTES, getPalette } from '../palettes'; +import { EUIAmsterdamColorBlindPalette } from '../palettes/eui_amsterdam'; +import { NeutralPalette } from '../palettes/neutral'; export const DEFAULT_NEUTRAL_PALETTE_INDEX = 1; @@ -31,7 +32,7 @@ export const DEFAULT_COLOR_MAPPING_CONFIG: ColorMapping.Config = { touched: false, }, ], - paletteId: EUIPalette.id, + paletteId: EUIAmsterdamColorBlindPalette.id, colorMode: { type: 'categorical', }, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts index 32c580f691215..86a8726296aa7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts @@ -9,8 +9,7 @@ export { CategoricalColorMapping, type ColorMappingProps } from './categorical_color_mapping'; export type { ColorMappingInputData } from './categorical_color_mapping'; export type { ColorMapping } from './config'; -export * from './palettes/available_palettes'; -export * from './palettes/default_palettes'; +export * from './palettes'; export * from './color/color_handling'; export { SPECIAL_TOKENS_STRING_CONVERTION } from './color/rule_matching'; export { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors } from './config/default_color_mapping'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts deleted file mode 100644 index 2a1d9057fc2be..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/available_palettes.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { ColorMapping } from '../config'; -import { - EUIPalette, - IKEAPalette, - NeutralPalette, - PastelPalette, - TableauPalette, -} from './default_palettes'; - -export const AVAILABLE_PALETTES = new Map([ - [EUIPalette.id, EUIPalette], - [TableauPalette.id, TableauPalette], - [IKEAPalette.id, IKEAPalette], - [PastelPalette.id, PastelPalette], - [NeutralPalette.id, NeutralPalette], -]); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts deleted file mode 100644 index a712c6a1f7f48..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/default_palettes.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { EUI_PALETTE_COLORS_DARK, EUI_PALETTE_COLORS_LIGHT } from './eui'; -import { TABLEAU_COLORS_DARK, TABLEAU_COLORS_LIGHT } from './tableau'; -import { IKEA_COLORS_DARK, IKEA_COLORS_LIGHT } from './ikea'; -import { PASTEL_PALETTE_DARK, PASTEL_PALETTE_LIGHT } from './pastel'; -import { NEUTRAL_COLOR_DARK, NEUTRAL_COLOR_LIGHT } from './neutral'; - -import { ColorMapping } from '../config'; - -export const EUIPalette: ColorMapping.CategoricalPalette = { - id: 'eui', - name: 'EUI', - colorCount: EUI_PALETTE_COLORS_LIGHT.length, - type: 'categorical', - getColor(valueInRange, isDarkMode) { - return isDarkMode - ? EUI_PALETTE_COLORS_DARK[valueInRange] - : EUI_PALETTE_COLORS_LIGHT[valueInRange]; - }, -}; - -export const TableauPalette: ColorMapping.CategoricalPalette = { - id: 'tableau', - name: 'Tableau', - colorCount: TABLEAU_COLORS_LIGHT.length, - type: 'categorical', - getColor(valueInRange, isDarkMode) { - return isDarkMode ? TABLEAU_COLORS_DARK[valueInRange] : TABLEAU_COLORS_LIGHT[valueInRange]; - }, -}; - -export const IKEAPalette: ColorMapping.CategoricalPalette = { - id: 'ikea', - name: 'IKEA', - colorCount: IKEA_COLORS_LIGHT.length, - type: 'categorical', - getColor(valueInRange, isDarkMode) { - return isDarkMode ? IKEA_COLORS_DARK[valueInRange] : IKEA_COLORS_LIGHT[valueInRange]; - }, -}; - -export const PastelPalette: ColorMapping.CategoricalPalette = { - id: 'pastel', - name: 'Pastel', - colorCount: PASTEL_PALETTE_LIGHT.length, - type: 'categorical', - getColor(valueInRange, isDarkMode) { - return isDarkMode ? PASTEL_PALETTE_DARK[valueInRange] : PASTEL_PALETTE_LIGHT[valueInRange]; - }, -}; - -export const NeutralPalette: ColorMapping.CategoricalPalette = { - id: 'neutral', - name: 'Neutral', - colorCount: NEUTRAL_COLOR_LIGHT.length, - type: 'categorical', - getColor(valueInRange, isDarkMode) { - return isDarkMode ? NEUTRAL_COLOR_DARK[valueInRange] : NEUTRAL_COLOR_LIGHT[valueInRange]; - }, -}; - -export function getPalette( - palettes: Map, - defaultPalette: ColorMapping.CategoricalPalette -) { - return (paletteId: string) => palettes.get(paletteId) ?? defaultPalette; -} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/elastic_brand.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/elastic_brand.ts new file mode 100644 index 0000000000000..d93440c5ac5e4 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/elastic_brand.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from '../config'; + +export const ELASTIC_BRAND_PALETTE_COLORS = [ + '#20377d', + '#7de2d1', + '#ff957d', + '#f04e98', + '#0077cc', + '#fec514', +]; + +export const ElasticBrandPalette: ColorMapping.CategoricalPalette = { + id: 'elastic_brand_2023', + name: 'Elastic Brand', + colorCount: ELASTIC_BRAND_PALETTE_COLORS.length, + type: 'categorical', + getColor(valueInRange) { + return ELASTIC_BRAND_PALETTE_COLORS[valueInRange]; + }, +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui_amsterdam.ts similarity index 55% rename from packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts rename to packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui_amsterdam.ts index 48d76861dbf21..ec48793e12819 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/eui_amsterdam.ts @@ -6,19 +6,9 @@ * Side Public License, v 1. */ -export const EUI_PALETTE_COLORS_LIGHT = [ - '#54b399', - '#6092c0', - '#d36086', - '#9170b8', - '#ca8eae', - '#d6bf57', - '#b9a888', - '#da8b45', - '#aa6556', - '#e7664c', -]; -export const EUI_PALETTE_COLORS_DARK = [ +import { ColorMapping } from '../config'; + +export const EUI_AMSTERDAM_PALETTE_COLORS = [ '#54b399', '#6092c0', '#d36086', @@ -30,3 +20,13 @@ export const EUI_PALETTE_COLORS_DARK = [ '#aa6556', '#e7664c', ]; + +export const EUIAmsterdamColorBlindPalette: ColorMapping.CategoricalPalette = { + id: 'eui_amsterdam_color_blind', + name: 'Default', + colorCount: EUI_AMSTERDAM_PALETTE_COLORS.length, + type: 'categorical', + getColor(valueInRange) { + return EUI_AMSTERDAM_PALETTE_COLORS[valueInRange]; + }, +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts deleted file mode 100644 index 2a83dc3e913c7..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/ikea.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export const IKEA_COLORS_LIGHT = ['#0057ad', '#af52b2', '#ff5688', '#ff8f48', '#fbda0c']; -export const IKEA_COLORS_DARK = ['#0057ad', '#af52b2', '#ff5688', '#ff8f48', '#fbda0c']; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palette.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/index.ts similarity index 55% rename from packages/kbn-coloring/src/shared_components/color_mapping/palette.ts rename to packages/kbn-coloring/src/shared_components/color_mapping/palettes/index.ts index 9ec94c9355977..340bbd32f0279 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palette.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/index.ts @@ -6,7 +6,18 @@ * Side Public License, v 1. */ -import { ColorMapping } from './config'; +import { ColorMapping } from '../config'; +import { ElasticBrandPalette } from './elastic_brand'; +import { EUIAmsterdamColorBlindPalette } from './eui_amsterdam'; +import { KibanaV7LegacyPalette } from './kibana_legacy'; +import { NeutralPalette } from './neutral'; + +export const AVAILABLE_PALETTES = new Map([ + [EUIAmsterdamColorBlindPalette.id, EUIAmsterdamColorBlindPalette], + [ElasticBrandPalette.id, ElasticBrandPalette], + [KibanaV7LegacyPalette.id, KibanaV7LegacyPalette], + [NeutralPalette.id, NeutralPalette], +]); /** * This function should be instanciated once at the root of the component with the available palettes and @@ -19,3 +30,8 @@ export function getPalette( ): (paletteId: string) => ColorMapping.CategoricalPalette { return (paletteId) => palettes.get(paletteId) ?? defaultPalette; } + +export * from './eui_amsterdam'; +export * from './elastic_brand'; +export * from './kibana_legacy'; +export * from './neutral'; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/kibana_legacy.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/kibana_legacy.ts new file mode 100644 index 0000000000000..9b576e0b05c66 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/kibana_legacy.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorMapping } from '../config'; + +export const KIBANA_V7_LEGACY_PALETTE_COLORS = [ + '#00a69b', + '#57c17b', + '#6f87d8', + '#663db8', + '#bc52bc', + '#9e3533', + '#daa05d', +]; + +export const KibanaV7LegacyPalette: ColorMapping.CategoricalPalette = { + id: 'kibana_v7_legacy', + name: 'Kibana Legacy', + colorCount: KIBANA_V7_LEGACY_PALETTE_COLORS.length, + type: 'categorical', + getColor(valueInRange) { + return KIBANA_V7_LEGACY_PALETTE_COLORS[valueInRange]; + }, +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts index eb432518f67b6..5d3d92790843b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/neutral.ts @@ -6,6 +6,18 @@ * Side Public License, v 1. */ +import { ColorMapping } from '../config'; + const schemeGreys = ['#f2f4fb', '#d4d9e5', '#98a2b3', '#696f7d', '#353642']; export const NEUTRAL_COLOR_LIGHT = schemeGreys.slice(); export const NEUTRAL_COLOR_DARK = schemeGreys.slice().reverse(); + +export const NeutralPalette: ColorMapping.CategoricalPalette = { + id: 'neutral', + name: 'Neutral', + colorCount: NEUTRAL_COLOR_LIGHT.length, + type: 'categorical', + getColor(valueInRange, isDarkMode) { + return isDarkMode ? NEUTRAL_COLOR_DARK[valueInRange] : NEUTRAL_COLOR_LIGHT[valueInRange]; + }, +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts deleted file mode 100644 index d4b5bbaaef85b..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/pastel.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export const PASTEL_PALETTE_LIGHT = [ - '#fbb4ae', - '#b3cde3', - '#ccebc5', - '#decbe4', - '#fed9a6', - '#ffffcc', - '#e5d8bd', - '#fddaec', -]; -export const PASTEL_PALETTE_DARK = [ - '#fbb4ae', - '#b3cde3', - '#ccebc5', - '#decbe4', - '#fed9a6', - '#ffffcc', - '#e5d8bd', - '#fddaec', -]; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts b/packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts deleted file mode 100644 index 23ffa4d7a6a30..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/palettes/tableau.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export const TABLEAU_COLORS_LIGHT = [ - '#4e79a7', - '#f28e2c', - '#e15759', - '#76b7b2', - '#59a14f', - '#edc949', - '#af7aa1', - '#ff9da7', - '#9c755f', - '#bab0ab', -]; -export const TABLEAU_COLORS_DARK = [ - '#4e79a7', - '#f28e2c', - '#e15759', - '#76b7b2', - '#59a14f', - '#edc949', - '#af7aa1', - '#ff9da7', - '#9c755f', - '#bab0ab', -]; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts b/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts index 7e9c852333f43..69bd57d2d852e 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/state/selectors.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { getPalette } from '../palette'; +import { getPalette } from '../palettes'; import { RootState } from './color_mapping'; export function selectPalette(getPaletteFn: ReturnType) { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index f1a279ba17c4c..84bd83092411a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -11,8 +11,8 @@ import { PaletteRegistry, getColorFactory, getPalette, - NeutralPalette, AVAILABLE_PALETTES, + NeutralPalette, } from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; diff --git a/x-pack/test/functional/apps/lens/group4/color_mapping.ts b/x-pack/test/functional/apps/lens/group4/color_mapping.ts index 8f480d11d51af..7d4b773cc4b2a 100644 --- a/x-pack/test/functional/apps/lens/group4/color_mapping.ts +++ b/x-pack/test/functional/apps/lens/group4/color_mapping.ts @@ -6,8 +6,12 @@ */ import expect from '@kbn/expect'; -import { PASTEL_PALETTE_LIGHT } from '@kbn/coloring/src/shared_components/color_mapping/palettes/pastel'; -import { EUI_PALETTE_COLORS_LIGHT } from '@kbn/coloring/src/shared_components/color_mapping/palettes/eui'; +import { + EUI_AMSTERDAM_PALETTE_COLORS, + ELASTIC_BRAND_PALETTE_COLORS, + EUIAmsterdamColorBlindPalette, + ElasticBrandPalette, +} from '@kbn/coloring'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { @@ -31,7 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', operation: 'terms', field: 'extension.raw', - palette: { mode: 'colorMapping', id: 'pastel' }, + palette: { mode: 'colorMapping', id: ElasticBrandPalette.id }, keepOpen: true, }); }); @@ -39,16 +43,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render correct color mapping', async () => { const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); const legendColors = chart?.legend?.items?.map((item) => item.color.toLowerCase()) ?? []; - expect(legendColors).to.eql(PASTEL_PALETTE_LIGHT.slice(0, 5).map((c) => c.toLowerCase())); + expect(legendColors).to.eql( + ELASTIC_BRAND_PALETTE_COLORS.slice(0, 5).map((c) => c.toLowerCase()) + ); }); it('should allow switching color mapping palette', async () => { await PageObjects.lens.changeColorMappingPalette( 'lnsXY_splitDimensionPanel > lnsLayerPanel-dimensionLink', - 'eui' + EUIAmsterdamColorBlindPalette.id ); const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); const legendColors = chart?.legend?.items?.map((item) => item.color.toLowerCase()) ?? []; - expect(legendColors).to.eql(EUI_PALETTE_COLORS_LIGHT.slice(0, 5).map((c) => c.toLowerCase())); + expect(legendColors).to.eql( + ELASTIC_BRAND_PALETTE_COLORS.slice(0, 5).map((c) => c.toLowerCase()) + ); }); it('should change categorical color', async () => { @@ -59,7 +67,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); const firstLegendItemColor = chart?.legend?.items?.[0]?.color?.toLowerCase() ?? 'NONE'; - expect(firstLegendItemColor).to.eql(EUI_PALETTE_COLORS_LIGHT[3].toLowerCase()); + expect(firstLegendItemColor).to.eql(EUI_AMSTERDAM_PALETTE_COLORS[3].toLowerCase()); }); }); } From 2b889e8a2a3ad58c81b0e741c2a538d00102a43d Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 19 Sep 2023 15:52:53 +0200 Subject: [PATCH 56/71] fix partition with no specified buckets --- .../public/utils/layers/get_layers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts index 84bd83092411a..6f40097809e18 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/layers/get_layers.ts @@ -137,12 +137,12 @@ function getColorFromMappingFactory( // return undefined, we will use the legacy color mapping instead return undefined; } - // if pie/donut/treemap multimetric or has no buckets use the default color mode + // if pie/donut/treemap with no buckets use the default color mode if ( (chartType === ChartTypes.DONUT || chartType === ChartTypes.PIE || chartType === ChartTypes.TREEMAP) && - (!dimensions.buckets || dimensions.buckets?.length === 0 || dimensions.metrics.length > 1) + (!dimensions.buckets || dimensions.buckets?.length === 0) ) { return undefined; } From c875cab3d8618a7aee6b689d4142dcdf794aaec4 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 19 Sep 2023 18:55:44 +0200 Subject: [PATCH 57/71] fix snapshot test after palette id change --- .../lens/public/visualizations/xy/visualization.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx index ef48cbc06f693..c9d52d43df7ef 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.tsx @@ -57,6 +57,7 @@ import { } from './visualization_helpers'; import { cloneDeep } from 'lodash'; import { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; +import { EUIAmsterdamColorBlindPalette } from '@kbn/coloring'; const DATE_HISTORGRAM_COLUMN_ID = 'date_histogram_column'; const exampleAnnotation: EventAnnotationConfig = { @@ -227,7 +228,7 @@ describe('xy_visualization', () => { "colorMode": Object { "type": "categorical", }, - "paletteId": "eui", + "paletteId": "${EUIAmsterdamColorBlindPalette.id}", "specialAssignments": Array [ Object { "color": Object { From 135f8eaa4ba7ad275d71d1872bd6e18bd2b4fef3 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 20 Sep 2023 16:16:03 +0200 Subject: [PATCH 58/71] fix imports in functional tests --- x-pack/test/functional/apps/lens/group4/color_mapping.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/group4/color_mapping.ts b/x-pack/test/functional/apps/lens/group4/color_mapping.ts index 7d4b773cc4b2a..87921882783cd 100644 --- a/x-pack/test/functional/apps/lens/group4/color_mapping.ts +++ b/x-pack/test/functional/apps/lens/group4/color_mapping.ts @@ -11,7 +11,7 @@ import { ELASTIC_BRAND_PALETTE_COLORS, EUIAmsterdamColorBlindPalette, ElasticBrandPalette, -} from '@kbn/coloring'; +} from '@kbn/coloring/src/shared_components/color_mapping/palettes'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { From 400fd4dedd9f04c371eaabffe40927e825ff334e Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 25 Sep 2023 15:35:34 +0200 Subject: [PATCH 59/71] improve style based on review comments --- .../__stories__/color_mapping.stories.tsx | 125 +++++++ .../components/assignment/assignment.tsx | 23 +- .../components/assignment/match.tsx | 3 +- .../assignment/special_assignment.tsx | 8 +- .../components/color_picker/color_picker.tsx | 62 +++- .../components/color_picker/color_swatch.tsx | 84 +++-- .../color_picker/palette_colors.tsx | 161 ++++---- .../components/color_picker/rgb_picker.tsx | 16 +- .../components/container/container.tsx | 226 +++++++----- .../components/palette_selector/gradient.scss | 9 +- .../components/palette_selector/gradient.tsx | 345 +++++++++++------- .../palette_selector/palette_selector.tsx | 14 +- .../palette_selector/scale_categorical.svg | 4 + .../palette_selector/scale_sequential.svg | 3 + .../color_mapping/state/color_mapping.ts | 15 + .../partition/dimension_editor.tsx | 174 ++++----- .../tagcloud/tags_dimension_editor.tsx | 172 ++++----- .../xy/xy_config_panel/dimension_editor.tsx | 171 ++++----- 18 files changed, 1002 insertions(+), 613 deletions(-) create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.svg create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx new file mode 100644 index 0000000000000..cccbf6fa9394e --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FC } from 'react'; +import { EuiFlyout, EuiForm } from '@elastic/eui'; +import { ComponentStory } from '@storybook/react'; +import { CategoricalColorMapping, ColorMappingProps } from '../categorical_color_mapping'; +import { AVAILABLE_PALETTES } from '../palettes'; +import { DEFAULT_COLOR_MAPPING_CONFIG } from '../config/default_color_mapping'; + +export default { + title: 'Color Mapping', + component: CategoricalColorMapping, + decorators: [ + (story: Function) => ( + {}} hideCloseButton> + {story()} + + ), + ], +}; + +const Template: ComponentStory> = (args) => ( + +); + +export const Default = Template.bind({}); + +Default.args = { + model: { + ...DEFAULT_COLOR_MAPPING_CONFIG, + assignmentMode: 'manual', + colorMode: { + type: 'gradient', + steps: [ + { + type: 'categorical', + colorIndex: 0, + paletteId: DEFAULT_COLOR_MAPPING_CONFIG.paletteId, + touched: false, + }, + { + type: 'categorical', + colorIndex: 1, + paletteId: DEFAULT_COLOR_MAPPING_CONFIG.paletteId, + touched: false, + }, + { + type: 'categorical', + colorIndex: 2, + paletteId: DEFAULT_COLOR_MAPPING_CONFIG.paletteId, + touched: false, + }, + ], + sort: 'asc', + }, + assignments: [ + { + rule: { + type: 'matchExactly', + values: ['this is', 'a multi-line combobox that is very long and that will be truncated'], + }, + color: { + type: 'gradient', + }, + touched: false, + }, + { + rule: { + type: 'matchExactly', + values: ['b'], + }, + color: { + type: 'gradient', + }, + touched: false, + }, + { + rule: { + type: 'matchExactly', + values: ['c'], + }, + color: { + type: 'gradient', + }, + touched: false, + }, + { + rule: { + type: 'matchExactly', + values: ['this is', 'a multi-line wrap', 'combo box', 'test combo', '3 lines'], + }, + color: { + type: 'gradient', + }, + touched: false, + }, + ], + }, + isDarkMode: false, + data: { + type: 'categories', + categories: [ + 'a', + 'b', + 'c', + 'd', + 'this is', + 'a multi-line wrap', + 'combo box', + 'test combo', + '3 lines', + ], + }, + + palettes: AVAILABLE_PALETTES, + specialTokens: new Map(), + // eslint-disable-next-line no-console + onModelUpdate: (model) => console.log(model), +}; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index 2480d2acc6b90..fbd94eebdd082 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -10,6 +10,8 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { removeAssignment, updateAssignmentColor, @@ -53,7 +55,12 @@ export function Assignment({ const dispatch = useDispatch(); return ( - + diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx index b04c2c40680e7..59e865d539571 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/match.tsx @@ -46,7 +46,7 @@ export const Match: React.FC<{ }); return ( - + ); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx index 272331cfe8eb8..374c997c54ece 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -33,7 +33,7 @@ export function SpecialAssignment({ const dispatch = useDispatch(); const canPickColor = true; return ( - + - + - - setTab('palette')} isSelected={tab === 'palette'}> - {i18n.translate('coloring.colorMapping.colorPicker.paletteTabLabel', { - defaultMessage: 'Palette', - })} - - setTab('custom')} isSelected={tab === 'custom'}> - {i18n.translate('coloring.colorMapping.colorPicker.customTabLabel', { - defaultMessage: 'Custom', - })} - - - +
+ + + setTab('palette')} isSelected={tab === 'palette'}> + + + {i18n.translate('coloring.colorMapping.colorPicker.paletteTabLabel', { + defaultMessage: 'Colors', + })} + + + + setTab('custom')} isSelected={tab === 'custom'}> + + + {i18n.translate('coloring.colorMapping.colorPicker.customTabLabel', { + defaultMessage: 'Custom', + })} + + + + + {tab === 'palette' ? ( - - + { close(); deleteStep(); }} + style={{ paddingBottom: 8 }} > {i18n.translate('coloring.colorMapping.colorPicker.removeGradientColorButtonLabel', { - defaultMessage: 'Remove from gradient', + defaultMessage: 'Remove color step', })} - + ) : null}
diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index c99738542ee02..7ea4565f9744b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -6,10 +6,18 @@ * Side Public License, v 1. */ -import { EuiColorPickerSwatch, EuiPopover } from '@elastic/eui'; +import { + EuiColorPickerSwatch, + EuiPopover, + euiShadowSmall, + isColorDark, + useEuiTheme, +} from '@elastic/eui'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; +import classNames from 'classnames'; +import { css } from '@emotion/react'; import { ColorPicker } from './color_picker'; import { getAssignmentColor } from '../../color/color_handling'; import { ColorMapping } from '../../config'; @@ -18,6 +26,8 @@ import { removeGradientColorStep } from '../../state/color_mapping'; import { selectColorPickerVisibility } from '../../state/selectors'; import { colorPickerVisibility, hideColorPickerVisibility } from '../../state/ui'; +import { getValidColor } from '../../color/color_math'; + interface ColorPickerSwatchProps { colorMode: ColorMapping.Config['colorMode']; assignmentColor: @@ -60,34 +70,58 @@ export const ColorSwatch = ({ index, total ); - + const colorIsDark = isColorDark(...getValidColor(colorHex).rgb()); + const euiTheme = useEuiTheme(); return canPickColor && assignmentColor.type !== 'gradient' ? ( dispatch(hideColorPickerVisibility())} anchorPosition="upLeft" button={ - dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} - style={{ - ...(swatchShape === 'round' ? { borderRadius: '50%', width: 15, height: 15 } : {}), - // the color swatch doesn't work here... - backgroundColor: colorHex, - cursor: canPickColor ? 'pointer' : 'not-allowed', - }} - className={ - swatchShape === 'round' - ? 'colorMappingColorSwatchEditableRound' - : 'colorMappingColorSwatchEditable' - } - /> + swatchShape === 'round' ? ( + ); } diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx index c37b79043e75e..5782715380ca7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/palette_selector.tsx @@ -16,8 +16,10 @@ import { EuiFlexItem, EuiFormRow, } from '@elastic/eui'; - import { i18n } from '@kbn/i18n'; +import scaleCategoricalIcon from './scale_categorical.svg'; +import scaleSequentialIcon from './scale_sequential.svg'; + import { RootState, updatePalette } from '../../state/color_mapping'; import { ColorMapping } from '../../config'; import { updateAssignmentsPalette, updateColorModePalette } from '../../config/assignments'; @@ -174,11 +176,11 @@ export function PaletteSelector({ <> {preserveChangesModal} {colorScaleModal} - + - + diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.svg b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.svg new file mode 100644 index 0000000000000..539cff0ee0f8f --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg new file mode 100644 index 0000000000000..52979a4ebed6a --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts index ad7f2380b8257..27588aff2b389 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/state/color_mapping.ts @@ -150,9 +150,24 @@ export const colorMappingSlice = createSlice({ } const steps = [...state.colorMode.steps]; steps.splice(action.payload, 1); + + // this maintain the correct sort direciton depending on which step + // gets removed from the array when only 2 steps are left. + const sort = + state.colorMode.steps.length === 2 + ? state.colorMode.sort === 'desc' + ? action.payload === 0 + ? 'asc' + : 'desc' + : action.payload === 0 + ? 'desc' + : 'asc' + : state.colorMode.sort; + state.colorMode = { ...state.colorMode, steps: [...steps], + sort, }; }, addGradientColorStep: ( diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index 2b434b9adcd13..2b9f8bd5e0d1a 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -19,7 +19,8 @@ import { } from '@kbn/coloring'; import { ColorPicker, useDebouncedValue } from '@kbn/visualization-ui-components'; import { - EuiButtonEmpty, + EuiFormRow, + EuiButtonIcon, EuiColorPaletteDisplay, EuiFlexGroup, EuiFlexItem, @@ -131,92 +132,97 @@ export function DimensionEditor(props: DimensionEditorProps) { return ( <> {props.accessor === firstNonCollapsedColumnId && ( - - - { - setIsPaletteOpen(!isPaletteOpen); - }} - /> - - - { - setIsPaletteOpen(!isPaletteOpen); - }} - size="xs" - flush="both" - > - {i18n.translate('xpack.lens.paletteXYGradient.customize', { - defaultMessage: 'Edit', - })} - - setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { - defaultMessage: 'Color', - })} - > -
- - - { - trackUiCounterEvents( - `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` - ); - setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); - setUseNewColorMapping(checked); - }} - /> - - - {canUseColorMapping || useNewColorMapping ? ( - setColorMapping(model)} - palettes={AVAILABLE_PALETTES} - data={{ - type: 'categories', - categories: splitCategories, - }} - specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} - /> - ) : ( - { - setLocalState({ ...props.state, palette: newPalette }); + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + /> + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + size="xs" + /> + setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.colorMapping.colorMappingPanelTitle', { + defaultMessage: 'Edit color by term mapping', + })} + > +
+ + + { + trackUiCounterEvents( + `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` + ); + setColorMapping( + checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined + ); + setUseNewColorMapping(checked); }} /> - )} - - -
-
-
-
+
+ + {canUseColorMapping || useNewColorMapping ? ( + setColorMapping(model)} + palettes={AVAILABLE_PALETTES} + data={{ + type: 'categories', + categories: splitCategories, + }} + specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} + /> + ) : ( + { + setLocalState({ ...props.state, palette: newPalette }); + }} + /> + )} + +
+
+
+
+
+
)} {/* TODO: understand how this works */} {showColorPicker && ( diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index 43774b75a63d9..d9aeb90a40e17 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -18,11 +18,12 @@ import { } from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { - EuiButtonEmpty, + EuiButtonIcon, EuiColorPaletteDisplay, EuiFlexGroup, EuiFlexItem, EuiSwitch, + EuiFormRow, } from '@elastic/eui'; import { useState, MutableRefObject, useCallback } from 'react'; import { PalettePicker } from '@kbn/coloring/src/shared_components/coloring/palette_picker'; @@ -86,91 +87,94 @@ export function TagsDimensionEditor({ const canUseColorMapping = state.colorMapping; return ( - - - { - setIsPaletteOpen(!isPaletteOpen); - }} - /> - - - { - setIsPaletteOpen(!isPaletteOpen); - }} - size="xs" - flush="both" - > - {i18n.translate('xpack.lens.paletteXYGradient.customize', { - defaultMessage: 'Edit', - })} - - setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { - defaultMessage: 'Color', - })} - > -
- - - { - trackUiCounterEvents( - `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` - ); - setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); - setUseNewColorMapping(checked); - }} - /> - - - {canUseColorMapping || useNewColorMapping ? ( - setColorMapping(model)} - palettes={AVAILABLE_PALETTES} - data={{ - type: 'categories', - categories: splitCategories, + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + /> + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + size="xs" + /> + setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', { + defaultMessage: 'Edit color by term mapping', + })} + > +
+ + + { + trackUiCounterEvents( + `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` + ); + setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); + setUseNewColorMapping(checked); }} - specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} /> - ) : ( - { - setPalette(newPalette); - }} - /> - )} - - -
-
-
-
+
+ + {canUseColorMapping || useNewColorMapping ? ( + setColorMapping(model)} + palettes={AVAILABLE_PALETTES} + data={{ + type: 'categories', + categories: splitCategories, + }} + specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} + /> + ) : ( + { + setPalette(newPalette); + }} + /> + )} + +
+
+
+
+
+ ); } diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index e3194d1f538ff..aa7ebf3329e49 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -11,8 +11,8 @@ import { useDebouncedValue } from '@kbn/visualization-ui-components'; import { ColorPicker } from '@kbn/visualization-ui-components'; import { - EuiButtonEmpty, EuiButtonGroup, + EuiButtonIcon, EuiColorPaletteDisplay, EuiFlexGroup, EuiFlexItem, @@ -157,92 +157,95 @@ export function DataDimensionEditor( if (props.groupId === 'breakdown' && !layer.collapseFn) { return ( - - - { - setIsPaletteOpen(!isPaletteOpen); - }} - /> - - - { - setIsPaletteOpen(!isPaletteOpen); - }} - size="xs" - flush="both" - > - {i18n.translate('xpack.lens.paletteXYGradient.customize', { - defaultMessage: 'Edit', - })} - - setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.table.colorByTermsPanelTitle', { - defaultMessage: 'Color', - })} - > -
- - - { - trackUiCounterEvents( - `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` - ); - setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); - setUseNewColorMapping(checked); - }} - /> - - - {canUseColorMapping || useNewColorMapping ? ( - setColorMapping(model)} - palettes={AVAILABLE_PALETTES} - data={{ - type: 'categories', - categories: splitCategories, + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + /> + + + { + setIsPaletteOpen(!isPaletteOpen); + }} + size="xs" + /> + setIsPaletteOpen(!isPaletteOpen)} + title={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', { + defaultMessage: 'Edit color by term mapping', + })} + > +
+ + + { + trackUiCounterEvents( + `color_mapping_switch_${checked ? 'enabled' : 'disabled'}` + ); + setColorMapping(checked ? { ...DEFAULT_COLOR_MAPPING_CONFIG } : undefined); + setUseNewColorMapping(checked); }} - specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} /> - ) : ( - { - setPalette(newPalette); - }} - /> - )} - - -
-
-
-
+
+ + {canUseColorMapping || useNewColorMapping ? ( + setColorMapping(model)} + palettes={AVAILABLE_PALETTES} + data={{ + type: 'categories', + categories: splitCategories, + }} + specialTokens={SPECIAL_TOKENS_STRING_CONVERTION} + /> + ) : ( + { + setPalette(newPalette); + }} + /> + )} + +
+
+
+
+
+ ); } From dda05ce9ac194bbdffc082b9933e6e5f1db67a6b Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 25 Sep 2023 15:57:01 +0200 Subject: [PATCH 60/71] use emotion instead of scss --- .../components/color_picker/color_swatch.tsx | 41 ++++++++++--------- .../components/palette_selector/gradient.scss | 33 --------------- .../components/palette_selector/gradient.tsx | 3 -- 3 files changed, 21 insertions(+), 56 deletions(-) delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index 7ea4565f9744b..bf79ed156487c 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -16,7 +16,6 @@ import { import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; -import classNames from 'classnames'; import { css } from '@emotion/react'; import { ColorPicker } from './color_picker'; import { getAssignmentColor } from '../../color/color_handling'; @@ -108,18 +107,26 @@ export const ColorSwatch = ({ data-test-subj={`lns-colorMapping-colorSwatch-${index}`} onClick={() => dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} style={{ - width: 32, - height: 32, // the color swatch can't pickup colors written in rgb/css standard backgroundColor: colorHex, cursor: canPickColor ? 'pointer' : 'not-allowed', + width: 32, + height: 32, }} - className={classNames( - 'colorMappingColorSwatchEditable', - colorIsDark - ? 'colorMappingColorSwatchEditableWhite' - : 'colorMappingColorSwatchEditableBlack' - )} + css={css` + &::after { + content: ''; + width: 0; + height: 0; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-top: 4px solid ${colorIsDark ? 'white' : 'black'}; + margin: 0; + bottom: 2px; + position: absolute; + right: 2px; + } + `} /> ) } @@ -152,20 +159,14 @@ export const ColorSwatch = ({ aria-label={i18n.translate('coloring.colorMapping.colorPicker.newColorAriaLabel', { defaultMessage: 'Select a new color', })} + disabled style={{ - ...(swatchShape === 'round' - ? { borderRadius: '50%', width: 15, height: 15 } - : { width: 32, height: 32 }), - // the color swatch doesn't work here... + // the color swatch can't pickup colors written in rgb/css standard backgroundColor: colorHex, - cursor: canPickColor ? 'pointer' : 'not-allowed', + cursor: 'not-allowed', + width: 32, + height: 32, }} - disabled - className={ - swatchShape === 'round' - ? 'colorMappingColorSwatchEditableRound' - : 'colorMappingColorSwatchEditable' - } /> ); }; diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss deleted file mode 100644 index e1f137a553bff..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.scss +++ /dev/null @@ -1,33 +0,0 @@ -.colorMappingGradientAddStop:hover { - color: #696F7D !important; -} -.colorMappingColorSwatchEditable::after { - content: ''; - width: 0; - height: 0; - border-left: 3px solid transparent; - border-right: 3px solid transparent; - border-top: 4px solid rgb(255,255,255); - margin: 0; - bottom: 2px; - position: absolute; - right: 2px; -} -.colorMappingColorSwatchEditableWhite::after { - border-top-color: white; -} -.colorMappingColorSwatchEditableBlack::after { - border-top-color: black; -} -.colorMappingColorSwatchEditableRound::after { - content: ''; - width: 0; - height: 0; - border-left: 3px solid transparent; - border-right: 3px solid transparent; - border-top: 4px solid rgb(255,255,255); - margin: 0; - bottom: 4px; - position: absolute; - right: 5px; -} \ No newline at end of file diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx index e9b645ced6e04..9d268d581add8 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -19,8 +19,6 @@ import { ColorMapping } from '../../config'; import { ColorSwatch } from '../color_picker/color_swatch'; import { getPalette } from '../../palettes'; -import './gradient.scss'; - import { addGradientColorStep, updateGradientColorStep } from '../../state/color_mapping'; import { colorPickerVisibility } from '../../state/ui'; @@ -252,7 +250,6 @@ function AddStop({ padding: 0 0.5px; ${euiFocusRing(euiTheme)}; `} - className="colorMappingGradientAddStop" onClick={() => { dispatch( addGradientColorStep({ From d8e1b7a6f7d41b9e93634cb92d8f9ff244598652 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:02:50 +0000 Subject: [PATCH 61/71] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- packages/kbn-coloring/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-coloring/tsconfig.json b/packages/kbn-coloring/tsconfig.json index 370e6d8ec6c5a..315e59225601c 100644 --- a/packages/kbn-coloring/tsconfig.json +++ b/packages/kbn-coloring/tsconfig.json @@ -21,6 +21,7 @@ "@kbn/shared-ux-utility", "@kbn/test-jest-helpers", "@kbn/data-plugin", + "@kbn/ui-theme", ], "exclude": [ "target/**/*", From 75a704bc33f080dd1f237b1fb4b74a3582793193 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 25 Sep 2023 18:10:32 +0200 Subject: [PATCH 62/71] fix test errors --- .../components/container/container.tsx | 3 ++- .../palette_selector/palette_selector.tsx | 8 ++++---- .../palette_selector/scale_categorical.svg | 4 ---- .../palette_selector/scale_categorical.tsx | 17 ++++++++++++++++ .../palette_selector/scale_sequential.svg | 3 --- .../palette_selector/scale_sequential.tsx | 20 +++++++++++++++++++ .../tagcloud/tags_dimension_editor.tsx | 2 +- .../xy/xy_config_panel/dimension_editor.tsx | 2 +- 8 files changed, 45 insertions(+), 14 deletions(-) delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.svg create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.tsx delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg create mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.tsx diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index b33550ffe5668..9218a56e720fd 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -120,6 +120,7 @@ export function Container(props: {
{ return (
- - - diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.tsx new file mode 100644 index 0000000000000..f71ed74485365 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_categorical.tsx @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; + +export function ScaleCategoricalIcon() { + return ( + + + + + ); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg deleted file mode 100644 index 52979a4ebed6a..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.tsx new file mode 100644 index 0000000000000..ec245f471f307 --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/scale_sequential.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; + +export function ScaleSequentialIcon() { + return ( + + + + ); +} diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index d9aeb90a40e17..a4fd5aecf7431 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -126,7 +126,7 @@ export function TagsDimensionEditor({ siblingRef={panelRef} isOpen={isPaletteOpen} handleClose={() => setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', { + title={i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { defaultMessage: 'Edit color by term mapping', })} > diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index aa7ebf3329e49..3f8e8c600489f 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -196,7 +196,7 @@ export function DataDimensionEditor( siblingRef={props.panelRef} isOpen={isPaletteOpen} handleClose={() => setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.colorMapping.editColorMappingButton', { + title={i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { defaultMessage: 'Edit color by term mapping', })} > From c7a8036f883e846f82610b0437a9e7f09e5df909 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 25 Sep 2023 22:02:17 +0200 Subject: [PATCH 63/71] fix FT palette id change --- .../components/color_picker/rgb_picker.tsx | 33 ++++++++----------- .../functional/apps/lens/group4/colors.ts | 9 ++--- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx index 322e8e8bceefb..0d68a5fe5f223 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; +import useDebounce from 'react-use/lib/useDebounce'; import { ColorMapping } from '../../config'; import { getColorContrast } from '../../color/color_math'; @@ -46,8 +47,19 @@ export function RGBPicker({ const lightContrast = getColorContrast(customColorHex, false); const darkContrast = getColorContrast(customColorHex, true); + + // debounce setting the color from the rgb picker by 500ms + useDebounce( + () => { + if (color !== customColor) { + selectColor(customColor); + } + }, + 500, + [color, customColor] + ); return ( - + Light Theme Contrast:{' '} @@ -74,25 +86,8 @@ export function RGBPicker({ color={customColorHex} display="inline" swatches={[]} - secondaryInputDisplay="bottom" /> - - - - { - if (color !== customColor) { - selectColor(customColor); - } else { - close(); - } - }} - > - {i18n.translate('coloring.colorMapping.colorPicker.useColorButtonLabel', { - defaultMessage: 'Select color', - })} - + ); diff --git a/x-pack/test/functional/apps/lens/group4/colors.ts b/x-pack/test/functional/apps/lens/group4/colors.ts index 56c8bfff753cf..20265d247cf83 100644 --- a/x-pack/test/functional/apps/lens/group4/colors.ts +++ b/x-pack/test/functional/apps/lens/group4/colors.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { ElasticBrandPalette } from '@kbn/coloring'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { @@ -58,11 +59,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', operation: 'terms', field: '@message.raw', - palette: { mode: 'colorMapping', id: 'pastel' }, + palette: { mode: 'colorMapping', id: ElasticBrandPalette.id }, keepOpen: true, }); - await PageObjects.lens.assertPalette('pastel', false); + await PageObjects.lens.assertPalette(ElasticBrandPalette.id, false); }); it('should carry over palette to the pie chart', async () => { @@ -70,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openDimensionEditor( 'lnsPie_sliceByDimensionPanel > lns-dimensionTrigger' ); - await PageObjects.lens.assertPalette('pastel', false); + await PageObjects.lens.assertPalette(ElasticBrandPalette.id, false); }); it('should carry palette back to the bar chart', async () => { @@ -78,7 +79,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.openDimensionEditor( 'lnsXY_splitDimensionPanel > lns-dimensionTrigger' ); - await PageObjects.lens.assertPalette('pastel', false); + await PageObjects.lens.assertPalette(ElasticBrandPalette.id, false); }); }); } From cf736adeb11d8a999951ea67ee91ed07c4c18d71 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 25 Sep 2023 20:35:15 +0000 Subject: [PATCH 64/71] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../components/color_picker/rgb_picker.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx index 0d68a5fe5f223..f5480cb6d9e80 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -6,16 +6,8 @@ * Side Public License, v 1. */ -import { - EuiButton, - EuiColorPicker, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiText, -} from '@elastic/eui'; +import { EuiColorPicker, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; import React, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; import useDebounce from 'react-use/lib/useDebounce'; import { ColorMapping } from '../../config'; From f87334361d32b0f1cfee12cc5eecace88b4c4b04 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 25 Sep 2023 22:42:45 +0200 Subject: [PATCH 65/71] fix wrong push --- .../color_mapping/components/color_picker/rgb_picker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx index f5480cb6d9e80..4a26404d12c18 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -78,8 +78,8 @@ export function RGBPicker({ color={customColorHex} display="inline" swatches={[]} + secondaryInputDisplay="bottom" /> - ); From 411e6af64c396c8e4852c73e75dac728223a89f7 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 26 Sep 2023 09:50:37 +0200 Subject: [PATCH 66/71] change color input warnings --- .../color_mapping/color/apca.ts | 140 ------------------ .../color_mapping/color/color_math.ts | 8 +- .../components/color_picker/rgb_picker.tsx | 114 ++++++++++---- 3 files changed, 88 insertions(+), 174 deletions(-) delete mode 100644 packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts deleted file mode 100644 index b1d3f5a2a812d..0000000000000 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/apca.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** @public */ -export type RGB = number; - -/** @public */ -export type A = number; - -/** @internal */ -export type RgbTuple = [r: RGB, g: RGB, b: RGB, alpha?: A]; - -/** @public */ -export type RgbaTuple = [r: RGB, g: RGB, b: RGB, alpha: A]; - -/** - * / // DO NOT use a Y from any other method ///// - * @internal - */ -export function APCAContrast([Rbg, Gbg, Bbg]: RgbTuple, [Rtxt, Gtxt, Btxt]: RgbTuple) { - // / // sRGB Conversion to Relative Luminance (Y) ///// - - const mainTRC = 2.4; // Transfer Curve (aka "Gamma") for sRGB linearization - // Simple power curve vs piecewise described in docs - // Essentially, 2.4 best models actual display - // characteristics in combination with the total method - - const Rco = 0.2126729; // sRGB Red Coefficient (from matrix) - const Gco = 0.7151522; // sRGB Green Coefficient (from matrix) - const Bco = 0.072175; // sRGB Blue Coefficient (from matrix) - - // / // For Finding Raw SAPC Contrast from Relative Luminance (Y) ///// - - const normBG = 0.56; // Constants for SAPC Power Curve Exponents - const normTXT = 0.57; // One pair for normal text, and one for reverse - const revTXT = 0.62; // These are the "beating heart" of SAPC - const revBG = 0.65; - - // / // For Clamping and Scaling Values ///// - // constant updated to https://github.com/Myndex/SAPC-APCA#apca-math-new-098g-4g-constants - // new 0.98G 4g constants - - const blkThrs = 0.022; // Level that triggers the soft black clamp - const blkClmp = 1.414; // Exponent for the soft black clamp curve - const deltaYmin = 0.0005; // Lint trap - const scaleBoW = 1.14; // Scaling for dark text on light - const scaleWoB = 1.14; // Scaling for light text on dark - const loConThresh = 0.035991; // Threshold for new simple offset scale - const loConFactor = 27.7847239587675; - const loConOffset = 0.027; // The simple offset - const loClip = 0.001; // Output clip (lint trap #2) - - // We are only concerned with Y at this point - // Ybg and Ytxt: divide sRGB to 0.0-1.0 range, linearize, - // and then apply the standard coefficients and sum to Y. - // Note that the Y we create here is unique and designed - // exclusively for SAPC. Do not use Y from other methods. - - let Ybg = - Math.pow(Rbg / 255.0, mainTRC) * Rco + - Math.pow(Gbg / 255.0, mainTRC) * Gco + - Math.pow(Bbg / 255.0, mainTRC) * Bco; - - let Ytxt = - Math.pow(Rtxt / 255.0, mainTRC) * Rco + - Math.pow(Gtxt / 255.0, mainTRC) * Gco + - Math.pow(Btxt / 255.0, mainTRC) * Bco; - - let SAPC = 0.0; // For holding raw SAPC values - let outputContrast = 0.0; // For weighted final values - - // / // TUTORIAL ///// - - // Take Y and soft clamp black, return 0 for very close luminances - // determine polarity, and calculate SAPC raw contrast - // Then apply the output scaling - - // Note that reverse contrast (white text on black) - // intentionally returns a negative number - // Proper polarity is important! - - // / /////// BLACK SOFT CLAMP & INPUT CLIP //////////////////////////////// - - // Soft clamp Y when near black. - // Now clamping all colors to prevent crossover errors - Ytxt = Ytxt > blkThrs ? Ytxt : Ytxt + Math.pow(blkThrs - Ytxt, blkClmp); - - Ybg = Ybg > blkThrs ? Ybg : Ybg + Math.pow(blkThrs - Ybg, blkClmp); - - // / // Return 0 Early for extremely low ∆Y (lint trap #1) ///// - if (Math.abs(Ybg - Ytxt) < deltaYmin) { - return 0.0; - } - - // / /////// SAPC CONTRAST /////////////////////////////////////////////// - - if (Ybg > Ytxt) { - // For normal polarity, black text on white - - // / // Calculate the SAPC contrast value and scale - - SAPC = (Math.pow(Ybg, normBG) - Math.pow(Ytxt, normTXT)) * scaleBoW; - - // / // NEW! SAPC SmoothScale™ - // Low Contrast Smooth Scale Rollout to prevent polarity reversal - // and also a low clip for very low contrasts (lint trap #2) - // much of this is for very low contrasts, less than 10 - // therefore for most reversing needs, only loConOffset is important - outputContrast = - SAPC < loClip - ? 0.0 - : SAPC < loConThresh - ? SAPC - SAPC * loConFactor * loConOffset - : SAPC - loConOffset; - } else { - // For reverse polarity, light text on dark - // WoB should always return negative value. - - SAPC = (Math.pow(Ybg, revBG) - Math.pow(Ytxt, revTXT)) * scaleWoB; - - outputContrast = - SAPC > -loClip - ? 0.0 - : SAPC > -loConThresh - ? SAPC - SAPC * loConFactor * loConOffset - : SAPC + loConOffset; - } - - return outputContrast * 100; -} // Close APCAcontrast() - -// / /\ ///////////////////////////////////////////\ -// / //\ END OF SAPC/APCA BLOCK /////////////////////////////////////////////\ -// / ///////////////////////////////////////////////////////////////////////////\ -// / ////////////////////////////////////////////////////////////////////////////\ diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts index 71415581601f7..eb9e57d52af55 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_math.ts @@ -7,7 +7,6 @@ */ import chroma from 'chroma-js'; -import { APCAContrast } from './apca'; export function getValidColor(color: string): chroma.Color { try { @@ -17,11 +16,8 @@ export function getValidColor(color: string): chroma.Color { } } -export function getColorContrast(color: string, isDark: boolean) { - const [r, g, b] = getValidColor(color).rgb(); - // TODO: change background depending on theme - const value = APCAContrast(isDark ? [0, 0, 0] : [255, 255, 255], [r, g, b]); - return { value: value.toFixed(1), contrast: Math.abs(value) > 40 }; +export function hasEnoughContrast(color: string, isDark: boolean, threshold = 4.5) { + return chroma.contrast(getValidColor(color), isDark ? 'black' : 'white') >= threshold; } export function changeAlpha(color: string, alpha: number) { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx index 4a26404d12c18..63db994bb0d21 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/rgb_picker.tsx @@ -6,12 +6,15 @@ * Side Public License, v 1. */ -import { EuiColorPicker, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; +import { EuiColorPicker, EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import React, { useState } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; +import chromajs from 'chroma-js'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; import { ColorMapping } from '../../config'; -import { getColorContrast } from '../../color/color_math'; +import { hasEnoughContrast } from '../../color/color_math'; import { getPalette } from '../../palettes'; export function RGBPicker({ @@ -28,59 +31,114 @@ export function RGBPicker({ selectColor: (color: ColorMapping.CategoricalColor | ColorMapping.ColorCode) => void; close: () => void; }) { - const [customColor, setCustomColor] = useState< + const [customColorMappingColor, setCustomColorMappingColor] = useState< ColorMapping.CategoricalColor | ColorMapping.ColorCode >(color); const customColorHex = - customColor.type === 'categorical' - ? getPaletteFn(customColor.paletteId).getColor(customColor.colorIndex, isDarkMode) - : customColor.colorCode; + customColorMappingColor.type === 'categorical' + ? getPaletteFn(customColorMappingColor.paletteId).getColor( + customColorMappingColor.colorIndex, + isDarkMode + ) + : customColorMappingColor.colorCode; - const lightContrast = getColorContrast(customColorHex, false); - const darkContrast = getColorContrast(customColorHex, true); + const [colorTextInput, setColorTextInput] = useState(customColorHex); + + // check contrasts with WCAG 2.1 with a min contrast ratio of 3 + const lightContrast = hasEnoughContrast(customColorHex, false, 3); + const darkContrast = hasEnoughContrast(customColorHex, true, 3); + + const errorMessage = [ + lightContrast === false ? 'light' : undefined, + darkContrast === false ? 'dark' : undefined, + ].filter(Boolean); + + const isColorTextValid = chromajs.valid(colorTextInput); + const colorHasContrast = lightContrast && darkContrast; // debounce setting the color from the rgb picker by 500ms useDebounce( () => { - if (color !== customColor) { - selectColor(customColor); + if (color !== customColorMappingColor) { + selectColor(customColorMappingColor); } }, 500, - [color, customColor] + [color, customColorMappingColor] ); return ( - - - Light Theme Contrast:{' '} - -
- Dark Theme Contrast:{' '} - -
-
{ - setCustomColor({ + setCustomColorMappingColor({ type: 'colorCode', colorCode: c, }); + setColorTextInput(c); }} color={customColorHex} display="inline" swatches={[]} - secondaryInputDisplay="bottom" /> + +
+ 1 ? 's' : '' + }` + : undefined + } + > + { + const textColor = e.currentTarget.value; + setColorTextInput(textColor); + if (chromajs.valid(textColor)) { + setCustomColorMappingColor({ + type: 'colorCode', + colorCode: chromajs(textColor).hex(), + }); + } + }} + aria-label="hex color input" + /> + +
+
); } From 71449a3b70e7c952982dccb192370b560d23e0ca Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 26 Sep 2023 15:25:43 +0200 Subject: [PATCH 67/71] fix tech preview badge and tests --- .../__stories__/color_mapping.stories.tsx | 11 +- .../components/assignment/assignment.tsx | 3 + .../components/assignment/match.tsx | 21 +- .../assignment/special_assignment.tsx | 8 +- .../components/color_picker/color_swatch.tsx | 16 +- .../components/container/container.tsx | 12 ++ .../components/palette_selector/gradient.tsx | 180 +++++++++--------- .../partition/dimension_editor.tsx | 29 ++- .../tagcloud/tags_dimension_editor.tsx | 29 ++- .../xy/xy_config_panel/dimension_editor.tsx | 31 ++- .../apps/lens/group4/color_mapping.ts | 2 +- .../test/functional/page_objects/lens_page.ts | 2 + 12 files changed, 233 insertions(+), 111 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx index cccbf6fa9394e..95f4ff5623ea3 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/__stories__/color_mapping.stories.tsx @@ -73,7 +73,7 @@ Default.args = { { rule: { type: 'matchExactly', - values: ['b'], + values: ['b', ['double', 'value']], }, color: { type: 'gradient', @@ -93,7 +93,14 @@ Default.args = { { rule: { type: 'matchExactly', - values: ['this is', 'a multi-line wrap', 'combo box', 'test combo', '3 lines'], + values: [ + 'this is', + 'a multi-line wrap', + 'combo box', + 'test combo', + '3 lines', + ['double', 'value'], + ], }, color: { type: 'gradient', diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx index fbd94eebdd082..896f2ea392884 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/assignment.tsx @@ -38,6 +38,7 @@ export function Assignment({ getPaletteFn, isDarkMode, specialTokens, + assignmentValuesCounter, }: { data: ColorMappingInputData; index: number; @@ -51,6 +52,7 @@ export function Assignment({ editable: boolean; isDarkMode: boolean; specialTokens: Map; + assignmentValuesCounter: Map; }) { const dispatch = useDispatch(); @@ -96,6 +98,7 @@ export function Assignment({ }) ); }} + assignmentValuesCounter={assignmentValuesCounter} /> ) : assignment.rule.type === 'range' ? ( ) => void; options: Array; specialTokens: Map; -}> = ({ index, rule, updateValue, editable, options, specialTokens }) => { + assignmentValuesCounter: Map; +}> = ({ index, rule, updateValue, editable, options, specialTokens, assignmentValuesCounter }) => { const selectedOptions = rule.type === 'auto' ? [] : typeof rule.values === 'string' - ? [{ label: rule.values, value: rule.values }] + ? [ + { + label: rule.values, + value: rule.values, + append: + (assignmentValuesCounter.get(rule.values) ?? 0) > 1 ? ( + + ) : undefined, + }, + ] : rule.values.map((value) => { const ruleValues = Array.isArray(value) ? value : [value]; return { label: ruleValues.map((v) => specialTokens.get(v) ?? v).join(MULTI_FIELD_KEY_SEPARATOR), value, + append: + (assignmentValuesCounter.get(value) ?? 0) > 1 ? ( + + ) : undefined, }; }); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx index 374c997c54ece..29ede59e37f41 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/assignment/special_assignment.tsx @@ -65,12 +65,12 @@ export function SpecialAssignment({ compressed fullWidth disabled={true} - placeholder={i18n.translate('coloring.colorMapping.assignments.allOtherPlaceholder', { - defaultMessage: 'All other terms', + placeholder={i18n.translate('coloring.colorMapping.assignments.unassignedPlaceholder', { + defaultMessage: 'Unassigned terms', })} - aria-label={i18n.translate('coloring.colorMapping.assignments.allOtherAriaLabel', { + aria-label={i18n.translate('coloring.colorMapping.assignments.unassignedAriaLabel', { defaultMessage: - 'Assign this color to all other terms not described in the assignment list', + 'Assign this color to every unassigned not described in the assignment list', })} /> diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx index bf79ed156487c..8ddc56d2476c7 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/color_picker/color_swatch.tsx @@ -85,7 +85,13 @@ export const ColorSwatch = ({ defaultMessage: 'Pick a color', })} data-test-subj={`lns-colorMapping-colorSwatch-${index}`} - onClick={() => dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} + onClick={() => + dispatch( + colorPickerVisible + ? hideColorPickerVisibility() + : colorPickerVisibility({ index, visible: true, type: forType }) + ) + } css={css` background: ${colorHex}; width: 16px; @@ -105,7 +111,13 @@ export const ColorSwatch = ({ defaultMessage: 'Pick a color', })} data-test-subj={`lns-colorMapping-colorSwatch-${index}`} - onClick={() => dispatch(colorPickerVisibility({ index, visible: true, type: forType }))} + onClick={() => + dispatch( + colorPickerVisible + ? hideColorPickerVisibility() + : colorPickerVisibility({ index, visible: true, type: forType }) + ) + } style={{ // the color swatch can't pickup colors written in rgb/css standard backgroundColor: colorHex, diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx index 9218a56e720fd..748f17fa45842 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/container/container.tsx @@ -76,6 +76,17 @@ export function Container(props: { const canAddNewAssignment = !autoAssignmentMode && assignments.length < MAX_ASSIGNABLE_COLORS; + const assignmentValuesCounter = assignments.reduce>( + (acc, assignment) => { + const values = assignment.rule.type === 'matchExactly' ? assignment.rule.values : []; + values.forEach((value) => { + acc.set(value, (acc.get(value) ?? 0) + 1); + }); + return acc; + }, + new Map() + ); + return ( @@ -160,6 +171,7 @@ export function Container(props: { assignment={assignment} disableDelete={assignments.length <= 1 || autoAssignmentMode} specialTokens={props.specialTokens} + assignmentValuesCounter={assignmentValuesCounter} />
); diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx index 9d268d581add8..c24c99158dc5b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -81,27 +81,29 @@ export function Gradient({ return ( <> -
+ {assignmentsSize > 1 && ( +
+ )}
-
+ {assignmentsSize > 1 && (
- {middleMostColorSep ? ( - - ) : colorMode.steps.length === 2 ? ( - - ) : undefined} +
+ {middleMostColorSep ? ( + + ) : colorMode.steps.length === 2 ? ( + + ) : undefined} +
-
-
1 && ( +
+ ${[ + gradientColorScale((assignmentsSize - 1) / assignmentsSize), + gradientColorScale(1), + ].join(',')} + ); + top: -4px; + height: 24px; + width: 6px; + margin-left: 5px; + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-left: 1px solid ${changeAlpha(euiThemeVars.euiColorDarkestShade, 0.2)}; + border-bottom: 1px solid ${changeAlpha(euiThemeVars.euiColorDarkestShade, 0.2)}; + border-right: 1px solid ${changeAlpha(euiThemeVars.euiColorDarkestShade, 0.2)}; + `} + /> + )}
setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.colorMapping.colorMappingPanelTitle', { - defaultMessage: 'Edit color by term mapping', - })} + title={ + useNewColorMapping + ? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { + defaultMessage: 'Edit colors by term mapping', + }) + : i18n.translate('xpack.lens.colorMapping.editColorsTitle', { + defaultMessage: 'Edit colors', + }) + } >
+ + {i18n.translate('xpack.lens.colorMapping.tryLabel', { + defaultMessage: 'Use the new Color Mapping feature', + })}{' '} + + {i18n.translate('xpack.lens.colorMapping.techPreviewLabel', { + defaultMessage: 'Tech preview', + })} + + + + } data-test-subj="lns_colorMappingOrLegacyPalette_switch" compressed checked={useNewColorMapping} diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index a4fd5aecf7431..4668ae8d49719 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -24,6 +24,8 @@ import { EuiFlexItem, EuiSwitch, EuiFormRow, + EuiText, + EuiBadge, } from '@elastic/eui'; import { useState, MutableRefObject, useCallback } from 'react'; import { PalettePicker } from '@kbn/coloring/src/shared_components/coloring/palette_picker'; @@ -126,15 +128,34 @@ export function TagsDimensionEditor({ siblingRef={panelRef} isOpen={isPaletteOpen} handleClose={() => setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { - defaultMessage: 'Edit color by term mapping', - })} + title={ + useNewColorMapping + ? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { + defaultMessage: 'Edit colors by term mapping', + }) + : i18n.translate('xpack.lens.colorMapping.editColorsTitle', { + defaultMessage: 'Edit colors', + }) + } >
+ + {i18n.translate('xpack.lens.colorMapping.tryLabel', { + defaultMessage: 'Use the new Color Mapping feature', + })}{' '} + + {i18n.translate('xpack.lens.colorMapping.techPreviewLabel', { + defaultMessage: 'Tech preview', + })} + + + + } data-test-subj="lns_colorMappingOrLegacyPalette_switch" compressed checked={useNewColorMapping} diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 3f8e8c600489f..6e6fed0f30229 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -11,13 +11,16 @@ import { useDebouncedValue } from '@kbn/visualization-ui-components'; import { ColorPicker } from '@kbn/visualization-ui-components'; import { + EuiBadge, EuiButtonGroup, EuiButtonIcon, EuiColorPaletteDisplay, EuiFlexGroup, EuiFlexItem, EuiFormRow, + EuiSpacer, EuiSwitch, + EuiText, htmlIdGenerator, } from '@elastic/eui'; import { @@ -196,15 +199,34 @@ export function DataDimensionEditor( siblingRef={props.panelRef} isOpen={isPaletteOpen} handleClose={() => setIsPaletteOpen(!isPaletteOpen)} - title={i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { - defaultMessage: 'Edit color by term mapping', - })} + title={ + useNewColorMapping + ? i18n.translate('xpack.lens.colorMapping.editColorMappingTitle', { + defaultMessage: 'Edit colors by term mapping', + }) + : i18n.translate('xpack.lens.colorMapping.editColorsTitle', { + defaultMessage: 'Edit colors', + }) + } >
+ + {i18n.translate('xpack.lens.colorMapping.tryLabel', { + defaultMessage: 'Use the new Color Mapping feature', + })}{' '} + + {i18n.translate('xpack.lens.colorMapping.techPreviewLabel', { + defaultMessage: 'Tech preview', + })} + + + + } data-test-subj="lns_colorMappingOrLegacyPalette_switch" compressed checked={useNewColorMapping} @@ -216,6 +238,7 @@ export function DataDimensionEditor( setUseNewColorMapping(checked); }} /> + {canUseColorMapping || useNewColorMapping ? ( diff --git a/x-pack/test/functional/apps/lens/group4/color_mapping.ts b/x-pack/test/functional/apps/lens/group4/color_mapping.ts index 87921882783cd..0fc9f79afec79 100644 --- a/x-pack/test/functional/apps/lens/group4/color_mapping.ts +++ b/x-pack/test/functional/apps/lens/group4/color_mapping.ts @@ -55,7 +55,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const chart = await PageObjects.lens.getCurrentChartDebugState('xyVisChart'); const legendColors = chart?.legend?.items?.map((item) => item.color.toLowerCase()) ?? []; expect(legendColors).to.eql( - ELASTIC_BRAND_PALETTE_COLORS.slice(0, 5).map((c) => c.toLowerCase()) + EUI_AMSTERDAM_PALETTE_COLORS.slice(0, 5).map((c) => c.toLowerCase()) ); }); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index e06fd01f560b4..3411d1a314068 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1906,6 +1906,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont await testSubjects.click(`lns-colorMapping-colorPicker-staticColor-${paletteColorIndex}`); + await testSubjects.click(`lns-colorMapping-colorSwatch-${colorSwatchIndex}`); + await this.closePaletteEditor(); await this.closeDimensionEditor(); From 0475e13b8e51ba2af747f521edaa9cf4b9b5cb09 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 27 Sep 2023 15:22:38 +0200 Subject: [PATCH 68/71] use tag cloud color mapping in suggestions --- .../visualizations/tagcloud/tagcloud_visualization.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx index a8cca5ab405d0..cf1c46d785daf 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx @@ -90,6 +90,15 @@ export const getTagcloudVisualization = ({ }, }; }, + getMainPalette: (state) => { + if (!state) return; + + return state.colorMapping + ? { type: 'colorMapping', value: state.colorMapping } + : state.palette + ? { type: 'legacyPalette', value: state.palette } + : undefined; + }, triggers: [VIS_EVENT_TO_TRIGGER.filter], From 6701a1f05580bb1e0c6090bd1ac71d46990d37af Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 27 Sep 2023 17:28:47 +0200 Subject: [PATCH 69/71] show correct dimension colors from color mapping --- .../color_mapping/color/color_handling.ts | 1 - .../config/default_color_mapping.ts | 44 +++++++++++++++++++ .../shared_components/color_mapping/index.ts | 6 ++- .../partition/dimension_editor.tsx | 4 +- .../partition/visualization.tsx | 4 +- .../tagcloud/tagcloud_visualization.tsx | 21 +++++++-- .../tagcloud/tags_dimension_editor.tsx | 4 +- .../visualizations/xy/visualization.tsx | 4 +- .../xy/xy_config_panel/dimension_editor.tsx | 4 +- 9 files changed, 76 insertions(+), 16 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index b60f1288e7c2f..a8e738ae4bbad 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -40,7 +40,6 @@ export function getAssignmentColor( ] : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); - // const colorScale = scaleSequential(piecewise(interp`olateLab, steps)); const colorScale = chroma.scale(steps).mode('lab'); return total === 0 ? 'red' diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index b377d92fbd33e..9736e171094ca 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -6,10 +6,13 @@ * Side Public License, v 1. */ +import chroma from 'chroma-js'; import { ColorMapping } from '.'; import { AVAILABLE_PALETTES, getPalette } from '../palettes'; import { EUIAmsterdamColorBlindPalette } from '../palettes/eui_amsterdam'; import { NeutralPalette } from '../palettes/neutral'; +import { changeAlpha, combineColors } from '../color/color_math'; +import { getColor } from '../color/color_handling'; export const DEFAULT_NEUTRAL_PALETTE_INDEX = 1; @@ -46,3 +49,44 @@ export function getPaletteColors( const palette = getPalette(AVAILABLE_PALETTES, NeutralPalette)(colorMappingModel.paletteId); return Array.from({ length: palette.colorCount }, (d, i) => palette.getColor(i, isDarkMode)); } + +export function getColorsFromMapping( + isDarkMode: boolean, + colorMappings?: ColorMapping.Config +): string[] { + const { colorMode, paletteId, assignmentMode, assignments, specialAssignments } = + colorMappings ?? { + ...DEFAULT_COLOR_MAPPING_CONFIG, + }; + + const getPaletteFn = getPalette(AVAILABLE_PALETTES, NeutralPalette); + if (colorMode.type === 'gradient') { + const steps = + colorMode.steps.length === 1 + ? [ + getColor(colorMode.steps[0], getPaletteFn, isDarkMode), + combineColors( + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + isDarkMode ? 'black' : 'white' + ), + ] + : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); + steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); + const colorScale = chroma.scale(steps).mode('lab'); + return Array.from({ length: 6 }, (d, i) => colorScale(i / 6).hex()); + } else { + const palette = getPaletteFn(paletteId); + if (assignmentMode === 'auto') { + return Array.from({ length: palette.colorCount }, (d, i) => palette.getColor(i, isDarkMode)); + } else { + return [ + ...assignments.map((a) => { + return a.color.type === 'gradient' ? '' : getColor(a.color, getPaletteFn, isDarkMode); + }), + ...specialAssignments.map((a) => { + return getColor(a.color, getPaletteFn, isDarkMode); + }), + ].filter((color) => color !== ''); + } + } +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts index 86a8726296aa7..1b49a2c6a8bf3 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/index.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/index.ts @@ -12,4 +12,8 @@ export type { ColorMapping } from './config'; export * from './palettes'; export * from './color/color_handling'; export { SPECIAL_TOKENS_STRING_CONVERTION } from './color/rule_matching'; -export { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors } from './config/default_color_mapping'; +export { + DEFAULT_COLOR_MAPPING_CONFIG, + getPaletteColors, + getColorsFromMapping, +} from './config/default_color_mapping'; diff --git a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx index 788d6283bb340..7274f170c4336 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/dimension_editor.tsx @@ -12,10 +12,10 @@ import { CategoricalColorMapping, DEFAULT_COLOR_MAPPING_CONFIG, PaletteRegistry, - getPaletteColors, ColorMapping, SPECIAL_TOKENS_STRING_CONVERTION, AVAILABLE_PALETTES, + getColorsFromMapping, } from '@kbn/coloring'; import { ColorPicker, useDebouncedValue } from '@kbn/visualization-ui-components'; import { @@ -127,7 +127,7 @@ export function DimensionEditor(props: DimensionEditorProps) { }) : undefined; - const colors = getPaletteColors(false, currentLayer.colorMapping); + const colors = getColorsFromMapping(props.isDarkMode, currentLayer.colorMapping); const table = props.frame.activeData?.[currentLayer.layerId]; const splitCategories = getColorCategories(table?.rows ?? [], props.accessor); diff --git a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx index 8196f66546cf6..429089f743c33 100644 --- a/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/partition/visualization.tsx @@ -11,8 +11,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ColorMapping, DEFAULT_COLOR_MAPPING_CONFIG, - getPaletteColors, PaletteRegistry, + getColorsFromMapping, } from '@kbn/coloring'; import { ThemeServiceStart } from '@kbn/core/public'; import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public'; @@ -204,7 +204,7 @@ export const getPieVisualization = ({ .subscribe({ next(theme) { colors = state.layers[0]?.colorMapping - ? getPaletteColors(theme.darkMode, state.layers[0].colorMapping) + ? getColorsFromMapping(theme.darkMode, state.layers[0].colorMapping) : paletteService .get(state.palette?.name || 'default') .getCategoricalColors(10, state.palette?.params); diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx index cf1c46d785daf..c9449f2dad178 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tagcloud_visualization.tsx @@ -16,7 +16,7 @@ import { buildExpressionFunction, ExpressionFunctionTheme, } from '@kbn/expressions-plugin/common'; -import { PaletteRegistry, DEFAULT_COLOR_MAPPING_CONFIG } from '@kbn/coloring'; +import { PaletteRegistry, DEFAULT_COLOR_MAPPING_CONFIG, getColorsFromMapping } from '@kbn/coloring'; import { IconChartTagcloud } from '@kbn/chart-icons'; import { SystemPaletteExpressionFunctionDefinition } from '@kbn/charts-plugin/common'; import useObservable from 'react-use/lib/useObservable'; @@ -114,6 +114,21 @@ export const getTagcloudVisualization = ({ }, getConfiguration({ state }) { + const canUseColorMapping = state.colorMapping ? true : false; + let colors: string[] = []; + if (canUseColorMapping) { + kibanaTheme.theme$ + .subscribe({ + next(theme) { + colors = getColorsFromMapping(theme.darkMode, state.colorMapping); + }, + }) + .unsubscribe(); + } else { + colors = paletteService + .get(state.palette?.name || 'default') + .getCategoricalColors(10, state.palette?.params); + } return { groups: [ { @@ -127,9 +142,7 @@ export const getTagcloudVisualization = ({ { columnId: state.tagAccessor, triggerIconType: 'colorBy', - palette: paletteService - .get(state.palette?.name || 'default') - .getCategoricalColors(10, state.palette?.params), + palette: colors, }, ] : [], diff --git a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index 4668ae8d49719..1728e6240ad9f 100644 --- a/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -11,10 +11,10 @@ import { CategoricalColorMapping, DEFAULT_COLOR_MAPPING_CONFIG, ColorMapping, - getPaletteColors, SPECIAL_TOKENS_STRING_CONVERTION, PaletteOutput, AVAILABLE_PALETTES, + getColorsFromMapping, } from '@kbn/coloring'; import { i18n } from '@kbn/i18n'; import { @@ -61,7 +61,7 @@ export function TagsDimensionEditor({ const [isPaletteOpen, setIsPaletteOpen] = useState(false); const [useNewColorMapping, setUseNewColorMapping] = useState(state.colorMapping ? true : false); - const colors = getPaletteColors(false, state.colorMapping); + const colors = getColorsFromMapping(isDarkMode, state.colorMapping); const table = frame.activeData?.[state.layerId]; const splitCategories = getColorCategories(table?.rows ?? [], state.tagAccessor); diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index d76260251757d..812d74ddcb331 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -25,7 +25,7 @@ import type { EventAnnotationGroupConfig } from '@kbn/event-annotation-common'; import { isEqual } from 'lodash'; import { type AccessorConfig, DimensionTrigger } from '@kbn/visualization-ui-components'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { DEFAULT_COLOR_MAPPING_CONFIG, getPaletteColors } from '@kbn/coloring'; +import { DEFAULT_COLOR_MAPPING_CONFIG, getColorsFromMapping } from '@kbn/coloring'; import useObservable from 'react-use/lib/useObservable'; import { generateId } from '../../id_generator'; import { @@ -429,7 +429,7 @@ export const getXyVisualization = ({ kibanaTheme.theme$ .subscribe({ next(theme) { - colors = getPaletteColors(theme.darkMode, layer.colorMapping); + colors = getColorsFromMapping(theme.darkMode, layer.colorMapping); }, }) .unsubscribe(); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 6e6fed0f30229..44114e8d560ed 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -27,11 +27,11 @@ import { PaletteRegistry, ColorMapping, DEFAULT_COLOR_MAPPING_CONFIG, - getPaletteColors, CategoricalColorMapping, PaletteOutput, SPECIAL_TOKENS_STRING_CONVERTION, AVAILABLE_PALETTES, + getColorsFromMapping, } from '@kbn/coloring'; import { getColorCategories } from '@kbn/chart-expressions-common'; import type { VisualizationDimensionEditorProps } from '../../../types'; @@ -149,7 +149,7 @@ export function DataDimensionEditor( const localLayer: XYDataLayerConfig = layer; const colors = layer.colorMapping - ? getPaletteColors(props.darkMode, layer.colorMapping) + ? getColorsFromMapping(props.darkMode, layer.colorMapping) : props.paletteService .get(layer.palette?.name || 'default') .getCategoricalColors(10, layer.palette); From b492532796689e439a1e34353cf05f0dd9dc0abe Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 27 Sep 2023 17:41:20 +0200 Subject: [PATCH 70/71] reuse color scale generator --- .../color_mapping/color/color_handling.ts | 51 ++++++++++--------- .../components/palette_selector/gradient.tsx | 35 ++----------- .../config/default_color_mapping.ts | 19 ++----- .../color_mapping/config/types.ts | 17 ++++--- 4 files changed, 44 insertions(+), 78 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts index a8e738ae4bbad..795f94b740e9b 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.ts @@ -7,11 +7,12 @@ */ import chroma from 'chroma-js'; import { ColorMapping } from '../config'; -import { changeAlpha, combineColors } from './color_math'; +import { changeAlpha, combineColors, getValidColor } from './color_math'; import { generateAutoAssignmentsForCategories } from '../config/assignment_from_categories'; import { getPalette } from '../palettes'; import { ColorMappingInputData } from '../categorical_color_mapping'; import { ruleMatch } from './rule_matching'; +import { GradientColorMode } from '../config/types'; export function getAssignmentColor( colorMode: ColorMapping.Config['colorMode'], @@ -29,23 +30,8 @@ export function getAssignmentColor( if (colorMode.type === 'categorical') { return 'red'; } - const steps = - colorMode.steps.length === 1 - ? [ - getColor(colorMode.steps[0], getPaletteFn, isDarkMode), - combineColors( - changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), - isDarkMode ? 'black' : 'white' - ), - ] - : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); - steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); - const colorScale = chroma.scale(steps).mode('lab'); - return total === 0 - ? 'red' - : total === 1 - ? colorScale(0).hex() - : colorScale(index / (total - 1)).hex(); + const colorScale = getGradientColorScale(colorMode, getPaletteFn, isDarkMode); + return total === 0 ? 'red' : total === 1 ? colorScale(0) : colorScale(index / (total - 1)); } } } @@ -55,12 +41,9 @@ export function getColor( getPaletteFn: ReturnType, isDarkMode: boolean ) { - switch (color.type) { - case 'colorCode': - return color.colorCode; - case 'categorical': - return getPaletteFn(color.paletteId).getColor(color.colorIndex, isDarkMode); - } + return color.type === 'colorCode' + ? color.colorCode + : getValidColor(getPaletteFn(color.paletteId).getColor(color.colorIndex, isDarkMode)).hex(); } export function getColorFactory( @@ -159,3 +142,23 @@ export function getColorFactory( } }; } + +export function getGradientColorScale( + colorMode: GradientColorMode, + getPaletteFn: ReturnType, + isDarkMode: boolean +): (value: number) => string { + const steps = + colorMode.steps.length === 1 + ? [ + getColor(colorMode.steps[0], getPaletteFn, isDarkMode), + combineColors( + changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), + isDarkMode ? 'black' : 'white' + ), + ] + : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); + steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); + const scale = chroma.scale(steps).mode('lab'); + return (value: number) => scale(value).hex(); +} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx index c24c99158dc5b..c4e22f797deaa 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx +++ b/packages/kbn-coloring/src/shared_components/color_mapping/components/palette_selector/gradient.tsx @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import chroma from 'chroma-js'; import { euiFocusRing, EuiIcon, euiShadowSmall, useEuiTheme } from '@elastic/eui'; import React from 'react'; @@ -13,7 +12,7 @@ import { useDispatch } from 'react-redux'; import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/react'; -import { changeAlpha, getValidColor, combineColors } from '../../color/color_math'; +import { changeAlpha } from '../../color/color_math'; import { ColorMapping } from '../../config'; import { ColorSwatch } from '../color_picker/color_swatch'; @@ -21,6 +20,7 @@ import { getPalette } from '../../palettes'; import { addGradientColorStep, updateGradientColorStep } from '../../state/color_mapping'; import { colorPickerVisibility } from '../../state/ui'; +import { getGradientColorScale } from '../../color/color_handling'; export function Gradient({ paletteId, @@ -39,20 +39,7 @@ export function Gradient({ return null; } const currentPalette = getPaletteFn(paletteId); - const gradientDirection = () => (colorMode.sort === 'asc' ? -1 : 1); - - const gradientColorSteps = - colorMode.steps.length === 1 - ? [ - getColor(colorMode.steps[0], getPaletteFn, isDarkMode), - combineColors( - changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), - isDarkMode ? 'black' : 'white' - ), - ].sort(gradientDirection) - : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)).sort(gradientDirection); - - const gradientColorScale = chroma.scale(gradientColorSteps).mode('lab'); + const gradientColorScale = getGradientColorScale(colorMode, getPaletteFn, isDarkMode); const topMostColorStop = colorMode.sort === 'asc' @@ -316,11 +303,7 @@ function ColorStop({ getPaletteFn, isDarkMode, }: { - colorMode: { - type: 'gradient'; - steps: Array<(ColorMapping.CategoricalColor | ColorMapping.ColorCode) & { touched: boolean }>; - sort: 'asc' | 'desc'; - }; + colorMode: ColorMapping.GradientColorMode; step: ColorMapping.CategoricalColor | ColorMapping.ColorCode; index: number; currentPalette: ColorMapping.CategoricalPalette; @@ -351,13 +334,3 @@ function ColorStop({ /> ); } - -function getColor( - color: ColorMapping.CategoricalColor | ColorMapping.ColorCode, - getPaletteFn: ReturnType, - isDarkMode: boolean -) { - return color.type === 'colorCode' - ? color.colorCode - : getValidColor(getPaletteFn(color.paletteId).getColor(color.colorIndex, isDarkMode)).hex(); -} diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts index 9736e171094ca..e4005770b2883 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/default_color_mapping.ts @@ -6,13 +6,11 @@ * Side Public License, v 1. */ -import chroma from 'chroma-js'; import { ColorMapping } from '.'; import { AVAILABLE_PALETTES, getPalette } from '../palettes'; import { EUIAmsterdamColorBlindPalette } from '../palettes/eui_amsterdam'; import { NeutralPalette } from '../palettes/neutral'; -import { changeAlpha, combineColors } from '../color/color_math'; -import { getColor } from '../color/color_handling'; +import { getColor, getGradientColorScale } from '../color/color_handling'; export const DEFAULT_NEUTRAL_PALETTE_INDEX = 1; @@ -61,19 +59,8 @@ export function getColorsFromMapping( const getPaletteFn = getPalette(AVAILABLE_PALETTES, NeutralPalette); if (colorMode.type === 'gradient') { - const steps = - colorMode.steps.length === 1 - ? [ - getColor(colorMode.steps[0], getPaletteFn, isDarkMode), - combineColors( - changeAlpha(getColor(colorMode.steps[0], getPaletteFn, isDarkMode), 0.3), - isDarkMode ? 'black' : 'white' - ), - ] - : colorMode.steps.map((d) => getColor(d, getPaletteFn, isDarkMode)); - steps.sort(() => (colorMode.sort === 'asc' ? -1 : 1)); - const colorScale = chroma.scale(steps).mode('lab'); - return Array.from({ length: 6 }, (d, i) => colorScale(i / 6).hex()); + const colorScale = getGradientColorScale(colorMode, getPaletteFn, isDarkMode); + return Array.from({ length: 6 }, (d, i) => colorScale(i / 6)); } else { const palette = getPaletteFn(paletteId); if (assignmentMode === 'auto') { diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts b/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts index ec540d1bdec2a..59cb18435112d 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/config/types.ts @@ -122,15 +122,18 @@ export interface Assignment { touched: boolean; } +export interface CategoricalColorMode { + type: 'categorical'; +} +export interface GradientColorMode { + type: 'gradient'; + steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; + sort: 'asc' | 'desc'; +} + export interface Config { paletteId: string; - colorMode: - | { type: 'categorical' } - | { - type: 'gradient'; - steps: Array<(CategoricalColor | ColorCode) & { touched: boolean }>; - sort: 'asc' | 'desc'; - }; + colorMode: CategoricalColorMode | GradientColorMode; assignmentMode: 'auto' | 'manual'; assignments: Array< Assignment< From e662e2916564ff29fa6a01303d90971928a7d59f Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 27 Sep 2023 18:55:11 +0200 Subject: [PATCH 71/71] fix test color validate to hex --- .../color_mapping/color/color_handling.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts index 2e36547a98821..93896394daf41 100644 --- a/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts +++ b/packages/kbn-coloring/src/shared_components/color_mapping/color/color_handling.test.ts @@ -62,8 +62,8 @@ describe('Color mapping - color generation', () => { categories: ['cat1', 'cat2', 'cat3', 'cat4'], } ); - expect(colorFactory('cat1')).toBe('red'); - expect(colorFactory('cat2')).toBe('blue'); + expect(colorFactory('cat1')).toBe('#ff0000'); + expect(colorFactory('cat2')).toBe('#0000ff'); // return a palette color only up to the max number of color in the palette expect(colorFactory('cat3')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]); expect(colorFactory('cat4')).toBe(NEUTRAL_COLOR_LIGHT[DEFAULT_NEUTRAL_PALETTE_INDEX]);