Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/10755.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Update new color scheme for heatmap ([#10755](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/10755))
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import { createLabelLayer, getDataBound, addTransform, enhanceStyle } from './heatmap_chart_utils';
import {
createLabelLayer,
getDataBound,
addTransform,
enhanceStyle,
generateSchemeList,
} from './heatmap_chart_utils';
import { AggregationType, VisFieldType, ColorSchemas, ScaleType, VisColumn } from '../types';
import { DEFAULT_GREY } from '../theme/default_colors';
import { defaultHeatmapChartStyles, HeatmapLabels, HeatmapChartStyle } from './heatmap_vis_config';
Expand Down Expand Up @@ -328,3 +334,25 @@ describe('enhanceStyle', () => {
expect(markLayer).toEqual(baseMarkLayer);
});
});

describe('generateSchemeList', () => {
it('should generate default 11 colors with center color matching target', () => {
const result = generateSchemeList('#ff0000');
expect(result).toHaveLength(11);
expect(result[5]).toBe('#ff0000');
});

it('should create lighter colors on left side', () => {
const result = generateSchemeList('#808080');
expect(result[0]).toBe('#e4e4e4');
expect(result[1]).toBe('#d0d0d0');
expect(result[5]).toBe('#808080');
});

it('should create darker colors on right side', () => {
const result = generateSchemeList('#808080');
expect(result[5]).toBe('#808080');
expect(result[6]).toBe('#6c6c6c');
expect(result[7]).toBe('#585858');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import type { Encoding } from 'vega-lite/build/src/encoding';
import { AggregationType, VisColumn } from '../types';
import { AggregationType, VisColumn, ColorSchemas } from '../types';
import { HeatmapChartStyle } from './heatmap_vis_config';

import { getColors, DEFAULT_GREY } from '../theme/default_colors';
Expand Down Expand Up @@ -117,3 +117,75 @@ export const enhanceStyle = (
markLayer.encoding.color.scale.range = [DEFAULT_GREY, ...colorRange];
}
};

export function generateSchemeList(targetHex: string, n = 11, step = 20) {
function hexToRgb(hex: string) {
hex = hex.replace('#', '');
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
return {
r: parseInt(hex.slice(0, 2), 16),
g: parseInt(hex.slice(2, 4), 16),
b: parseInt(hex.slice(4, 6), 16),
};
}

function rgbToHex({ r, g, b }: { r: number; g: number; b: number }) {
return (
'#' +
r.toString(16).padStart(2, '0') +
g.toString(16).padStart(2, '0') +
b.toString(16).padStart(2, '0')
);
}

const half = Math.floor(n / 2);
const target = hexToRgb(targetHex);
const colors = [];

// Left side (lighter)
for (let i = half; i > 0; i--) {
colors.push(
rgbToHex({
r: Math.min(255, target.r + i * step),
g: Math.min(255, target.g + i * step),
b: Math.min(255, target.b + i * step),
})
);
}

// Center
colors.push(rgbToHex(target));

// Right side (darker)
for (let i = 1; i <= half; i++) {
colors.push(
rgbToHex({
r: Math.max(0, target.r - i * step),
g: Math.max(0, target.g - i * step),
b: Math.max(0, target.b - i * step),
})
);
}
return colors;
}

export const getColorRange = (colorSchema: ColorSchemas) => {
switch (colorSchema) {
case ColorSchemas.BLUES:
return generateSchemeList(getColors().categories[0]);
case ColorSchemas.PURPLES:
return generateSchemeList(getColors().categories[1]);
case ColorSchemas.ORANGES:
return generateSchemeList(getColors().categories[2]);
case ColorSchemas.YELLOWS:
return generateSchemeList(getColors().categories[3]);
case ColorSchemas.GREENS:
return generateSchemeList(getColors().categories[5]);
case ColorSchemas.REDS:
return generateSchemeList(getColors().categories[6]);
default:
return undefined;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jest.mock('./heatmap_chart_utils', () => ({
enhanceStyle: jest.fn(),
addTransform: jest.fn(() => []),
createLabelLayer: jest.fn(() => null),
getColorRange: jest.fn(() => undefined),
}));

jest.mock('../utils/utils', () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { HeatmapChartStyle } from './heatmap_vis_config';
import { VisColumn, VEGASCHEMA, AxisColumnMappings } from '../types';
import { applyAxisStyling, getSwappedAxisRole, getSchemaByAxis } from '../utils/utils';
import { createLabelLayer, enhanceStyle, addTransform } from './heatmap_chart_utils';
import { createLabelLayer, enhanceStyle, addTransform, getColorRange } from './heatmap_chart_utils';

export const createHeatmapWithBin = (
transformedData: Array<Record<string, any>>,
Expand Down Expand Up @@ -103,6 +103,10 @@ export const createRegularHeatmap = (
const colorField = colorFieldColumn?.column;
const colorName = colorFieldColumn?.name;

const colorScale = getColorRange(styles.exclusive?.colorSchema)
? { range: getColorRange(styles.exclusive?.colorSchema) }
: { scheme: styles.exclusive?.colorSchema };

const markLayer: any = {
mark: {
type: 'rect',
Expand Down Expand Up @@ -131,7 +135,7 @@ export const createRegularHeatmap = (
: false,
scale: {
type: styles.exclusive?.colorScaleType,
scheme: styles.exclusive?.colorSchema,
...colorScale,
reverse: styles.exclusive?.reverseSchema,
},
legend: styles.addLegend
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const getColors = () => {
// been implemented accordingly.
const isExperimental =
localStorage.getItem('__DEVELOPMENT__.discover.vis.theme') === 'experimental';

const euiPaletteColorBlindColors = euiPaletteColorBlind();

if (isExperimental) {
if (darkMode) {
return {
Expand Down Expand Up @@ -69,7 +72,18 @@ export const getColors = () => {
text: euiThemeVars.euiTextColor,
grid: euiThemeVars.euiColorChartLines,
backgroundShade: darkMode ? '#27252C' : '#f1f1f1ff',
categories: euiPaletteColorBlind(),
categories: [
euiPaletteColorBlindColors[1],
euiPaletteColorBlindColors[3],
euiPaletteColorBlindColors[9],
euiPaletteColorBlindColors[5],
euiPaletteColorBlindColors[1],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this duplicate color intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in the new theme categories, we’ve defined two types of blue: purple blue and regular blue. But euiPaletteColorBlindColors only includes one blue, I duplicated it to cover both cases.

euiPaletteColorBlindColors[0],
euiPaletteColorBlindColors[2],
euiPaletteColorBlindColors[8],
euiPaletteColorBlindColors[0],
euiPaletteColorBlindColors[4],
],
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,12 @@ export enum PointShape {

export enum ColorSchemas {
BLUES = 'blues',
PURPLES = 'purples',
ORANGES = 'oranges',
YELLOWS = 'yellows',
GREENS = 'greens',
GREYS = 'greys',
REDS = 'reds',
GREYS = 'greys',
YELLOWORANGE = 'yelloworangered',
GREENBLUE = 'greenblue',
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,35 @@ export const getColorSchemas = () => [
value: ColorSchemas.GREENS,
},
{
text: i18n.translate('explore.vis.colorSchemas.greys', {
defaultMessage: 'Greys',
text: i18n.translate('explore.vis.colorSchemas.purples', {
defaultMessage: 'Purples',
}),
value: ColorSchemas.GREYS,
value: ColorSchemas.PURPLES,
},
{
text: i18n.translate('explore.vis.colorSchemas.reds', {
defaultMessage: 'Reds',
}),
value: ColorSchemas.REDS,
},
{
text: i18n.translate('explore.vis.colorSchemas.greys', {
defaultMessage: 'Greys',
}),
value: ColorSchemas.GREYS,
},
{
text: i18n.translate('explore.vis.colorSchemas.yellows', {
defaultMessage: 'Yellows',
}),
value: ColorSchemas.YELLOWS,
},
{
text: i18n.translate('explore.vis.colorSchemas.oranges', {
defaultMessage: 'Oranges',
}),
value: ColorSchemas.ORANGES,
},
{
text: i18n.translate('explore.vis.colorSchemas.greenToBlue', {
defaultMessage: 'Green to Blue',
Expand Down