Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const heatmapSharedStateSchema = {
),
...sharedPanelInfoSchema,
...layerSettingsSchema,
axis: schema.maybe(
axes: schema.maybe(
schema.object(
{
x: schema.maybe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -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' } }
);
Expand All @@ -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' } }
);
Expand Down Expand Up @@ -305,6 +309,7 @@ export type ColorMappingCategoricalType = TypeOf<typeof categoricalColorMappingS
export type ColorMappingGradientType = TypeOf<typeof gradientColorMappingSchema>;
export type ColorMappingColorDefType = TypeOf<typeof colorDefSchema>;
export type AllColoringTypes = TypeOf<typeof allColoringTypeSchema>;
export type UnassignedColorType = TypeOf<typeof unassignedColorSchema>;
/**
* Schema for where to apply the color (to value or background).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ const layerSettingsSchemaWrapped = schema.object(layerSettingsSchema);
export type LayerSettingsSchema = TypeOf<typeof layerSettingsSchemaWrapped>;

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' } })),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<HeatmapGridConfigResult>({
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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function getOrientationFromRotation(rotation: number): 'angled' | 'vertical' | '

function getGridConfigProps(
gridConfig: HeatmapVisualizationState['gridConfig']
): HeatmapState['axis'] {
): HeatmapState['axes'] {
return {
x: {
labels: {
Expand All @@ -63,15 +63,15 @@ function getGridConfigProps(
}),
},
title: {
value: gridConfig.xTitle,
text: gridConfig.xTitle,
visible: gridConfig.isXAxisTitleVisible,
},
...(gridConfig.xSortPredicate ? { sort: gridConfig.xSortPredicate } : {}),
},
y: {
labels: { visible: gridConfig.isYAxisLabelVisible },
title: {
value: gridConfig.yTitle,
text: gridConfig.yTitle,
visible: gridConfig.isYAxisTitleVisible,
},
...(gridConfig.ySortPredicate ? { sort: gridConfig.ySortPredicate } : {}),
Expand All @@ -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 },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -259,13 +259,28 @@ function convertXExtent(extent: AxisExtentConfig | undefined): {
return {};
}

/**
* Returns the first map key whose value strictly equals the provided value.
*/
function findKeyByValue<T extends Record<string, unknown>>(
map: T,
value: unknown
): Extract<keyof T, string> | undefined {
return Object.keys(map).find((key): key is Extract<keyof T, string> => map[key] === value);
}

function convertAxisSettingsToAPIFormat(
config: XYPersistedState,
layers: Record<string, DataSourceStateLayer>
): Pick<XYState, 'axis'> | {} {
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)) {
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading