diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/heatmap.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/heatmap.ts index a914bf139ab9a..fedd8bd007d04 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/heatmap.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/heatmap.ts @@ -64,7 +64,7 @@ const heatmapSharedStateSchema = { ), ...sharedPanelInfoSchema, ...layerSettingsSchema, - axis: schema.maybe( + axes: schema.maybe( schema.object( { x: schema.maybe( diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/tagcloud.test.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/tagcloud.test.ts index 5b85ba07a3036..58451a12bde5a 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/tagcloud.test.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/charts/tagcloud.test.ts @@ -91,7 +91,7 @@ describe('Tagcloud Schema', () => { color: { type: 'from_palette', palette: 'default', index: 0 }, }, ], - unassignedColor: { type: 'color_code', value: '#cccccc' }, + unassigned: { type: 'color_code', value: '#cccccc' }, }, }, }; diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.test.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.test.ts index ed4d12f4b88ba..d1aa38e3428e8 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.test.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.test.ts @@ -9,7 +9,12 @@ import { freeze, produce } from 'immer'; -import type { ColorByValueStep, ColorByValueType, ColorMappingType } from './color'; +import type { + ColorByValueStep, + ColorByValueType, + ColorMappingCategoricalType, + ColorMappingType, +} from './color'; import { allColoringTypeSchema, colorByValueStepsSchema } from './color'; describe('Color Schema', () => { @@ -238,7 +243,7 @@ describe('Color Schema', () => { color: { type: 'color_code', value: 'red' }, }, ], - unassignedColor: { type: 'color_code', value: 'green' }, + unassigned: { type: 'color_code', value: 'green' }, }; const validated = allColoringTypeSchema.validate(input); @@ -271,7 +276,7 @@ describe('Color Schema', () => { color: { type: 'from_palette', palette: 'default', index: 0 }, }, ], - unassignedColor: { type: 'color_code', value: 'green' }, + unassigned: { type: 'color_code', value: 'green' }, }; const validated = allColoringTypeSchema.validate(input); @@ -354,7 +359,7 @@ describe('Color Schema', () => { palette: 'default', }, ], - unassignedColor: { type: 'color_code', value: 'green' }, + unassigned: { type: 'color_code', value: 'green' }, }; const validated = allColoringTypeSchema.validate(input); @@ -376,25 +381,15 @@ describe('Color Schema', () => { it('throws on invalid mode in color mapping', () => { const input = { palette: 'kibana_palette', + // @ts-expect-error mode: 'invalid', - colorMapping: { - values: ['value1'], - }, - otherColors: {}, - }; - - expect(() => allColoringTypeSchema.validate(input)).toThrow(); - }); - - it('throws on empty values array in categorical mapping', () => { - const input = { - palette: 'kibana_palette', - mode: 'categorical', - colorMapping: { - values: [], - }, - otherColors: {}, - }; + mapping: [ + { + values: ['value1'], + color: { type: 'color_code', value: '#FF00FF' }, + }, + ], + } satisfies ColorMappingCategoricalType; expect(() => allColoringTypeSchema.validate(input)).toThrow(); }); diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.ts index 0b0e320658afd..4290fffbbfc81 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/color.ts @@ -231,6 +231,10 @@ const colorCodeSchema = schema.object( const colorDefSchema = schema.oneOf([colorFromPaletteSchema, colorCodeSchema]); +const unassignedColorSchema = schema.oneOf([colorFromPaletteSchema, colorCodeSchema], { + meta: { description: 'The color to use for unassigned values.', id: 'unassignedColorSchema' }, +}); + const categoricalColorMappingSchema = schema.object( { mode: schema.literal('categorical'), @@ -244,7 +248,7 @@ const categoricalColorMappingSchema = schema.object( }), { maxSize: 1000 } ), - unassignedColor: schema.maybe(colorDefSchema), + unassigned: schema.maybe(unassignedColorSchema), }, { meta: { id: 'categoricalColorMapping', title: 'Categorical Color Mapping' } } ); @@ -269,7 +273,7 @@ const gradientColorMappingSchema = schema.object( ) ), gradient: schema.maybe(schema.arrayOf(colorDefSchema, { maxSize: 3 })), - unassignedColor: schema.maybe(colorDefSchema), + unassigned: schema.maybe(unassignedColorSchema), }, { meta: { id: 'gradientColorMapping', title: 'Gradient Color Mapping' } } ); @@ -305,6 +309,7 @@ export type ColorMappingCategoricalType = TypeOf; export type ColorMappingColorDefType = TypeOf; export type AllColoringTypes = TypeOf; +export type UnassignedColorType = TypeOf; /** * Schema for where to apply the color (to value or background). */ diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/shared.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/shared.ts index febc2b55d2cbf..7d989b4352409 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/shared.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/schema/shared.ts @@ -153,9 +153,7 @@ const layerSettingsSchemaWrapped = schema.object(layerSettingsSchema); export type LayerSettingsSchema = TypeOf; export const axisTitleSchemaProps = { - value: schema.maybe( - schema.string({ defaultValue: '', meta: { description: 'Axis title text' } }) - ), + text: schema.maybe(schema.string({ defaultValue: '', meta: { description: 'Axis title text' } })), visible: schema.maybe(schema.boolean({ meta: { description: 'Show the title' } })), }; diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/from_api.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/from_api.ts index eb82e2d958b74..0692d69d22c7d 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/from_api.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/from_api.ts @@ -53,7 +53,7 @@ function buildVisualizationState(config: HeatmapState): HeatmapVisualizationStat const layer = config; const valueAccessor = getAccessorName('value'); const basePalette = layer.metric.color && fromColorByValueAPIToLensState(layer.metric.color); - const xAxisLabelRotation = getRotationFromOrientation(layer.axis?.x?.labels?.orientation); + const xAxisLabelRotation = getRotationFromOrientation(layer.axes?.x?.labels?.orientation); return { layerId: DEFAULT_LAYER_ID, @@ -65,16 +65,16 @@ function buildVisualizationState(config: HeatmapState): HeatmapVisualizationStat gridConfig: { type: HEATMAP_GRID_NAME, isCellLabelVisible: layer.cells?.labels?.visible ?? false, - isXAxisLabelVisible: layer.axis?.x?.labels?.visible ?? true, - isXAxisTitleVisible: layer.axis?.x?.title?.visible ?? false, - isYAxisLabelVisible: layer.axis?.y?.labels?.visible ?? true, - isYAxisTitleVisible: layer.axis?.y?.title?.visible ?? false, + isXAxisLabelVisible: layer.axes?.x?.labels?.visible ?? true, + isXAxisTitleVisible: layer.axes?.x?.title?.visible ?? false, + isYAxisLabelVisible: layer.axes?.y?.labels?.visible ?? true, + isYAxisTitleVisible: layer.axes?.y?.title?.visible ?? false, ...stripUndefined({ - xTitle: layer.axis?.x?.title?.value, - yTitle: layer.axis?.y?.title?.value, + xTitle: layer.axes?.x?.title?.text, + yTitle: layer.axes?.y?.title?.text, xAxisLabelRotation, - xSortPredicate: layer.axis?.x?.sort, - ySortPredicate: layer.axis?.y?.sort, + xSortPredicate: layer.axes?.x?.sort, + ySortPredicate: layer.axes?.y?.sort, }), }, legend: { diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/to_api.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/to_api.ts index ee5c904e372de..fde2aeaeb027b 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/to_api.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/heatmap/to_api.ts @@ -53,7 +53,7 @@ function getOrientationFromRotation(rotation: number): 'angled' | 'vertical' | ' function getGridConfigProps( gridConfig: HeatmapVisualizationState['gridConfig'] -): HeatmapState['axis'] { +): HeatmapState['axes'] { return { x: { labels: { @@ -63,7 +63,7 @@ function getGridConfigProps( }), }, title: { - value: gridConfig.xTitle, + text: gridConfig.xTitle, visible: gridConfig.isXAxisTitleVisible, }, ...(gridConfig.xSortPredicate ? { sort: gridConfig.xSortPredicate } : {}), @@ -71,7 +71,7 @@ function getGridConfigProps( y: { labels: { visible: gridConfig.isYAxisLabelVisible }, title: { - value: gridConfig.yTitle, + text: gridConfig.yTitle, visible: gridConfig.isYAxisTitleVisible, }, ...(gridConfig.ySortPredicate ? { sort: gridConfig.ySortPredicate } : {}), @@ -96,7 +96,7 @@ function reverseBuildVisualizationState( ...generateApiLayer(layer), type: HEATMAP_NAME, legend: getLegendProps(visualization.legend), - axis: getGridConfigProps(visualization.gridConfig), + axes: getGridConfigProps(visualization.gridConfig), cells: { labels: { visible: visualization.gridConfig.isCellLabelVisible }, }, diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/xy/chart.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/xy/chart.ts index 6b10a2c16c6cf..03fc2ca2df2f0 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/xy/chart.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/charts/xy/chart.ts @@ -127,9 +127,9 @@ function convertAxisSettingsToStateFormat( yLeft: orientationDictionary[axis?.left?.labels?.orientation ?? 'horizontal'], yRight: orientationDictionary[axis?.right?.labels?.orientation ?? 'horizontal'], }; - const xTitle = axis?.x?.title?.value; - const yTitle = axis?.left?.title?.value; - const yRightTitle = axis?.right?.title?.value; + const xTitle = axis?.x?.title?.text; + const yTitle = axis?.left?.title?.text; + const yRightTitle = axis?.right?.title?.text; const yLeftScale = axis?.left?.scale; const yRightScale = axis?.right?.scale; return stripUndefined({ @@ -259,13 +259,28 @@ function convertXExtent(extent: AxisExtentConfig | undefined): { return {}; } +/** + * Returns the first map key whose value strictly equals the provided value. + */ +function findKeyByValue>( + map: T, + value: unknown +): Extract | undefined { + return Object.keys(map).find((key): key is Extract => map[key] === value); +} + function convertAxisSettingsToAPIFormat( config: XYPersistedState, layers: Record ): Pick | {} { const axis: EditableAxisType = {}; - let xAxisScale: string | undefined; + const { labelsOrientation } = config; + const xLabelsOrientation = findKeyByValue(orientationDictionary, labelsOrientation?.x); + const yLeftLabelsOrientation = findKeyByValue(orientationDictionary, labelsOrientation?.yLeft); + const yRightLabelsOrientation = findKeyByValue(orientationDictionary, labelsOrientation?.yRight); + + let xAxisScale: XAxisType['scale']; const firstLayer = config.layers[0]; const dataSourceLayer = layers[firstLayer.layerId]; if (isTextBasedLayer(dataSourceLayer) && isLensStateDataLayer(firstLayer)) { @@ -283,7 +298,7 @@ function convertAxisSettingsToAPIFormat( title: config.xTitle || config.axisTitlesVisibilitySettings?.x != null ? stripUndefined({ - value: config.xTitle, + text: config.xTitle, visible: config.axisTitlesVisibilitySettings?.x != null ? config.axisTitlesVisibilitySettings.x @@ -300,17 +315,9 @@ function convertAxisSettingsToAPIFormat( ? { visible: config.gridlinesVisibilitySettings.x } : undefined, ...convertXExtent(config.xExtent), - ...(config.labelsOrientation?.x != null - ? { - labels: { - orientation: Object.entries(orientationDictionary).find( - ([_, value]) => value === config.labelsOrientation?.x - )?.[0] as 'horizontal' | 'vertical' | 'angled' | undefined, - }, - } - : {}), + labels: xLabelsOrientation ? { orientation: xLabelsOrientation } : undefined, scale: xAxisScale, - }); + } satisfies XAxisType); if (Object.keys(xAxis).length) { axis.x = xAxis; } @@ -319,7 +326,7 @@ function convertAxisSettingsToAPIFormat( title: config.yTitle || config.axisTitlesVisibilitySettings?.yLeft != null ? stripUndefined({ - value: config.yTitle, + text: config.yTitle, visible: config.axisTitlesVisibilitySettings?.yLeft != null ? config.axisTitlesVisibilitySettings.yLeft @@ -336,16 +343,8 @@ function convertAxisSettingsToAPIFormat( ? { visible: config.gridlinesVisibilitySettings.yLeft } : undefined, ...convertExtendsToAPIFormat(config.yLeftExtent), - ...(config.labelsOrientation?.yLeft != null - ? { - labels: { - orientation: Object.entries(orientationDictionary).find( - ([_, value]) => value === config.labelsOrientation?.yLeft - )?.[0] as 'horizontal' | 'vertical' | 'angled' | undefined, - }, - } - : {}), - }); + labels: yLeftLabelsOrientation ? { orientation: yLeftLabelsOrientation } : undefined, + } satisfies YAxisType); if (Object.keys(leftAxis).length) { axis.left = leftAxis; } @@ -354,7 +353,7 @@ function convertAxisSettingsToAPIFormat( title: config.yRightTitle || config.axisTitlesVisibilitySettings?.yRight != null ? stripUndefined({ - value: config.yRightTitle, + text: config.yRightTitle, visible: config.axisTitlesVisibilitySettings?.yRight != null ? config.axisTitlesVisibilitySettings.yRight @@ -371,15 +370,7 @@ function convertAxisSettingsToAPIFormat( ? { visible: config.gridlinesVisibilitySettings.yRight } : undefined, ...convertExtendsToAPIFormat(config.yRightExtent), - ...(config.labelsOrientation?.yRight != null - ? { - labels: { - orientation: Object.entries(orientationDictionary).find( - ([_, value]) => value === config.labelsOrientation?.yRight - )?.[0] as 'horizontal' | 'vertical' | 'angled' | undefined, - }, - } - : {}), + labels: yRightLabelsOrientation ? { orientation: yRightLabelsOrientation } : undefined, }); if (Object.keys(rightAxis).length) { diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.test.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.test.ts index fae59622b34b9..c74a77a344061 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.test.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.test.ts @@ -828,7 +828,7 @@ describe('Color util transforms', () => { color: { type: 'color_code', value: '#ff0000' }, }, ], - unassignedColor: { type: 'color_code', value: '#00ff00' }, + unassigned: { type: 'color_code', value: '#00ff00' }, }; const lensState = fromColorMappingAPIToLensState(originalColorMapping); @@ -884,7 +884,7 @@ describe('Color util transforms', () => { { type: 'from_palette', index: 2, palette: 'no_default' }, ], sort: 'asc', - unassignedColor: { type: 'from_palette', palette: SEMANTIC_PALETTE, index: 2 }, + unassigned: { type: 'from_palette', palette: SEMANTIC_PALETTE, index: 2 }, }; const lensState = fromColorMappingAPIToLensState(originalColorMapping); diff --git a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.ts b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.ts index ad0ad89b3b23a..ba18849b2a9fb 100644 --- a/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.ts +++ b/src/platform/packages/shared/kbn-lens-embeddable-utils/config_builder/transforms/coloring/coloring.ts @@ -14,9 +14,12 @@ import type { ColorByValueAbsolute, ColorByValueStep, ColorByValueType, + ColorMappingCategoricalType, ColorMappingColorDefType, + ColorMappingGradientType, ColorMappingType, StaticColorType, + UnassignedColorType, } from '../../schema/color'; import type { SerializableValueType } from '../../schema/serializedValue'; @@ -274,12 +277,11 @@ function isLensStateCategoricalConfigColorMapping( function fromUnassignedColorLensStateToAPI( color: ColorMapping.CategoricalColor | ColorMapping.ColorCode | ColorMapping.LoopColor | undefined -): { unassignedColor: Extract } | {} { +): UnassignedColorType | undefined { if (!color || color.type === 'loop') { - return {}; + return undefined; } - const unassignedColor = fromColorLensStateToAPI(color); - return { unassignedColor }; + return fromColorLensStateToAPI(color); } export function fromColorMappingLensStateToAPI( @@ -291,15 +293,13 @@ export function fromColorMappingLensStateToAPI( mode: 'categorical', palette: `${LEGACY_PALETTE_PREFIX}${legacyPalette.name}`, mapping: [], - }; + } satisfies ColorMappingCategoricalType; } if (!colorMapping) { return; } - const unassignedColor = fromUnassignedColorLensStateToAPI( - colorMapping.specialAssignments[0]?.color - ); + const unassigned = fromUnassignedColorLensStateToAPI(colorMapping.specialAssignments[0]?.color); if (isLensStateCategoricalConfigColorMapping(colorMapping)) { return { mode: 'categorical', @@ -310,8 +310,8 @@ export function fromColorMappingLensStateToAPI( color: fromColorLensStateToAPI(color), }; }), - ...unassignedColor, - }; + ...(unassigned ? { unassigned } : {}), + } satisfies ColorMappingCategoricalType; } // because of early return above, we know it is a gradient at this point so casting is safe @@ -328,8 +328,8 @@ export function fromColorMappingLensStateToAPI( }), sort: (colorMapping.colorMode as ColorMapping.GradientColorMode).sort, gradient: colorMode.steps.map((color) => fromColorLensStateToAPI(color)), - ...unassignedColor, - }; + ...(unassigned ? { unassigned } : {}), + } satisfies ColorMappingGradientType; } function fromColorDefAPIToLensState( @@ -411,8 +411,8 @@ export function fromColorMappingAPIToLensState( type: 'other', }, ], - color: colorMapping.unassignedColor - ? fromColorDefAPIToLensState(colorMapping.unassignedColor) + color: colorMapping.unassigned + ? fromColorDefAPIToLensState(colorMapping.unassigned) : { type: 'loop' }, touched: false, },