Skip to content
Snippets Groups Projects
Commit ed9523e9 authored by Tadeusz Miesiąc's avatar Tadeusz Miesiąc
Browse files

Merge branch 'MIN-189/display-general-overlays' into 'development'

Resolve MIN-189 "/display general overlays"

Closes MIN-189

See merge request !78
parents f51cab97 b2282dfd
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!78Resolve MIN-189 "/display general overlays"
Pipeline #83443 passed
import { getHexTricolorGradientColorWithAlpha } from './getHexTricolorGradientColorWithAlpha';
const RED_HEX = '#ff0000';
const GREEN_HEX = '#00ff00';
const BLUE_HEX = '#0000ff';
describe('getHexTricolorGradientColorWithAlpha', () => {
const cases: [{ alpha: number | undefined; position: number }, string][] = [
[{ alpha: 1, position: -1 }, '#FF0000ff'],
[{ alpha: 0.8, position: -0.75 }, '#BF4000cc'],
[{ alpha: 0.5, position: -0.5 }, '#80800080'],
[{ alpha: 0, position: -0.25 }, '#40BF0000'],
[{ alpha: 1, position: 0 }, '#00FF00ff'],
[{ alpha: 1, position: 0.25 }, '#00BF40ff'],
[{ alpha: 1, position: 0.5 }, '#008080ff'],
[{ alpha: 1, position: 0.75 }, '#0040BFff'],
[{ alpha: 1, position: 1 }, '#0000FFff'],
];
it.each(cases)(`and position %s should return %s`, (input, output) => {
expect(
getHexTricolorGradientColorWithAlpha({
leftColor: RED_HEX,
middleColor: GREEN_HEX,
rightColor: BLUE_HEX,
alpha: input.alpha,
position: input.position,
}),
).toEqual(output);
});
});
import { interpolateThreeColors } from '../lerp/interpolateThreeColors';
import { addAlphaToHexString } from './addAlphaToHexString';
import { hexToRgb } from './hexToRgb';
import { rgbToHex } from './rgbToHex';
export type GetHexTricolorGradientColorWithAlphaProps = {
leftColor: string;
middleColor: string;
rightColor: string;
alpha: number | undefined;
position: number;
};
const WHITE_HEX_OPACITY_0 = '#00000000';
export const getHexTricolorGradientColorWithAlpha = ({
leftColor,
middleColor,
rightColor,
alpha,
position,
}: GetHexTricolorGradientColorWithAlphaProps): string => {
const leftRgb = hexToRgb(leftColor);
const middleRgb = hexToRgb(middleColor);
const rightRgb = hexToRgb(rightColor);
if (!leftRgb || !middleRgb || !rightRgb) {
return WHITE_HEX_OPACITY_0; // white, opacity 0
}
const interpolatedColor = interpolateThreeColors({
leftColor: leftRgb,
middleColor: middleRgb,
rightColor: rightRgb,
position,
});
const interpolatedHexColor = rgbToHex(interpolatedColor);
const interpolatedColorWithAlpha = addAlphaToHexString(interpolatedHexColor, alpha);
return interpolatedColorWithAlpha;
};
import { expandHexToFullFormatIfItsShorthanded, hexToRgb } from './hexToRgb';
describe('expandHexToFullFormatIfItsShorthanded', () => {
it('should expand short-handed hex string to full format', () => {
const result = expandHexToFullFormatIfItsShorthanded('#abc');
expect(result).toBe('aabbcc');
});
it('should not modify full-format hex string', () => {
const result = expandHexToFullFormatIfItsShorthanded('#aabbcc');
expect(result).toBe('#aabbcc');
});
it('should handle hex string without leading #', () => {
const result = expandHexToFullFormatIfItsShorthanded('abc');
expect(result).toBe('aabbcc');
});
it('should return original string if it does not match short-hand regex', () => {
const result = expandHexToFullFormatIfItsShorthanded('invalid');
expect(result).toBe('invalid');
});
});
describe('hexToRgb', () => {
it('should convert valid hex string to RGB object', () => {
const result = hexToRgb('#aabbcc');
expect(result).toEqual({ r: 170, g: 187, b: 204 });
});
it('should return null for invalid hex string', () => {
const result = hexToRgb('invalid');
expect(result).toBeNull();
});
it('should handle hex string without leading #', () => {
const result = hexToRgb('aabbcc');
expect(result).toEqual({ r: 170, g: 187, b: 204 });
});
it('should return null for hex string with invalid characters', () => {
const result = hexToRgb('#xyz123');
expect(result).toBeNull();
});
it('should convert short-handed RGB hex string without leading # to RGB object', () => {
const result = hexToRgb('abc'); // Short-handed RGB hex string without leading #
expect(result).toEqual({ r: 170, g: 187, b: 204 });
});
it('should handle short-handed RGB hex string with invalid characters', () => {
const result = hexToRgb('#xyz'); // Short-handed RGB hex string with invalid characters
expect(result).toBeNull();
});
it('should handle short-handed RGB hex string with invalid characters and without leading #', () => {
const result = hexToRgb('xyz'); // Short-handed RGB hex string with invalid characters and without leading #
expect(result).toBeNull();
});
});
/* eslint-disable no-magic-numbers */
export const expandHexToFullFormatIfItsShorthanded = (hexString: string): string => {
const SHORT_HAND_REGEX = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const fullHexString = hexString.replace(SHORT_HAND_REGEX, (m, r, g, b) => {
return r + r + g + g + b + b;
});
return fullHexString;
};
const FULL_HEX_REGEX = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
export const hexToRgb = (hexString: string): { r: number; g: number; b: number } | null => {
const fullHexString = expandHexToFullFormatIfItsShorthanded(hexString);
const result = FULL_HEX_REGEX.exec(fullHexString);
if (!result) {
return null;
}
return {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
};
};
import { rgbToHex } from './rgbToHex'; // Replace 'yourFileName' with the actual file name where your function is defined
describe('rgbToHex - util', () => {
it('should convert RGB values to hex format', () => {
// Test case 1: Black color
expect(rgbToHex({ r: 0, g: 0, b: 0 })).toBe('#000000');
// Test case 2: White color
expect(rgbToHex({ r: 255, g: 255, b: 255 })).toBe('#FFFFFF');
// Test case 3: Red color
expect(rgbToHex({ r: 255, g: 0, b: 0 })).toBe('#FF0000');
// Test case 4: Green color
expect(rgbToHex({ r: 0, g: 255, b: 0 })).toBe('#00FF00');
// Test case 5: Blue color
expect(rgbToHex({ r: 0, g: 0, b: 255 })).toBe('#0000FF');
// Test case 6: Custom color
expect(rgbToHex({ r: 128, g: 64, b: 32 })).toBe('#804020');
});
it('should handle invalid input values', () => {
// Test case 1: Negative RGB values
expect(() => rgbToHex({ r: -1, g: 0, b: 255 })).toThrow();
// Test case 2: RGB values exceeding 255
expect(() => rgbToHex({ r: 256, g: 128, b: 64 })).toThrow();
// Test case 3: Non-integer RGB values
expect(() => rgbToHex({ r: 50.5, g: 100.75, b: 150.25 })).toThrow();
});
});
import { RGBColor } from '@/types/colors';
const MIN_RGB_VALUE = 0;
const MAX_RGB_VALUE = 255;
const isCorrectRgbValue = ({ r, g, b }: RGBColor): boolean =>
!Number.isInteger(r) ||
!Number.isInteger(g) ||
!Number.isInteger(b) ||
r < MIN_RGB_VALUE ||
g < MIN_RGB_VALUE ||
b < MIN_RGB_VALUE ||
r > MAX_RGB_VALUE ||
g > MAX_RGB_VALUE ||
b > MAX_RGB_VALUE;
export const rgbToHex = ({ r, g, b }: RGBColor): string => {
if (isCorrectRgbValue({ r, g, b })) {
throw new Error('Invalid RGB values. Values must be integers between 0 and 255.');
}
// eslint-disable-next-line no-bitwise, no-magic-numbers
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
};
/* eslint-disable no-magic-numbers */
import { RGBColor } from '@/types/colors';
import { interpolateThreeColors } from './interpolateThreeColors';
const LEFT_COLOR = { r: 255, g: 0, b: 0 }; // Red
const MIDDLE_COLOR = { r: 0, g: 255, b: 0 }; // Green
const RIGHT_COLOR = { r: 0, g: 0, b: 255 }; // Blue
describe('interpolateThreeColors - util', () => {
const cases: [number, RGBColor][] = [
[-1, LEFT_COLOR],
[-0.75, { r: 191, g: 64, b: 0 }],
[-0.5, { r: 128, g: 128, b: 0 }],
[-0.25, { r: 64, g: 191, b: 0 }],
[0, MIDDLE_COLOR],
[0.25, { r: 0, g: 191, b: 64 }],
[0.5, { r: 0, g: 128, b: 128 }],
[0.75, { r: 0, g: 64, b: 191 }],
[1, RIGHT_COLOR],
];
it.each(cases)(
`for linear gradient with range [-1,0,1]: left color (-1) ${JSON.stringify(
LEFT_COLOR,
)}, middle color (0) ${JSON.stringify(MIDDLE_COLOR)} and right Color (1) ${JSON.stringify(
RIGHT_COLOR,
)} and position %s should return %s`,
(input, output) => {
expect(
interpolateThreeColors({
leftColor: LEFT_COLOR,
middleColor: MIDDLE_COLOR,
rightColor: RIGHT_COLOR,
position: input,
}),
).toEqual(output);
},
);
});
import { RGBColor } from '@/types/colors';
import { lerpRGBColor } from './lerpRGBColor';
const MIN_VAL = -1;
const MAX_VAL = 1;
const MIDDLE_VAL = 0;
type InterpolateColorProps = {
leftColor: RGBColor;
middleColor: RGBColor;
rightColor: RGBColor;
position: number;
};
/**
*
* @param {position} range [-1,1]
* function interpolates between linear gradient of 3 colors for given position
* -1 is value for leftColor
* 0 is value for middleColor
* 1 is value for rightColor
*/
export const interpolateThreeColors = ({
leftColor,
middleColor,
rightColor,
position,
}: InterpolateColorProps): RGBColor => {
const clampedPosition = Math.max(MIN_VAL, Math.min(MAX_VAL, position)); // make sure value is in [-1,1] range
if (clampedPosition < MIDDLE_VAL) {
/**
* -1 .......|... 0
* -0.25 - this is position
* BUT function interpolates on positive values so position must not be negative value
* 0 .............1
* 0.75
*/
// eslint-disable-next-line no-magic-numbers
return lerpRGBColor({ leftColor, rightColor: middleColor, position: 1 + clampedPosition });
}
return lerpRGBColor({ leftColor: middleColor, rightColor, position: clampedPosition });
};
/* eslint-disable no-magic-numbers */
import { lerpRGBColor } from './lerpRGBColor';
describe('interpolateColor', () => {
const leftColor = { r: 255, g: 0, b: 0 }; // Red
const rightColor = { r: 0, g: 255, b: 0 }; // Green
it('should return color1 for position 0', () => {
const result = lerpRGBColor({ leftColor, rightColor, position: 0 });
expect(result).toEqual(leftColor);
});
it('should return color2 for position 1', () => {
const result = lerpRGBColor({ leftColor, rightColor, position: 1 });
expect(result).toEqual(rightColor);
});
it('should interpolate colors for position 0.25', () => {
const result = lerpRGBColor({ leftColor, rightColor, position: 0.25 });
expect(result).toEqual({ r: 191, g: 64, b: 0 });
});
it('should interpolate colors for position 0.5', () => {
const result = lerpRGBColor({ leftColor, rightColor, position: 0.5 });
expect(result).toEqual({ r: 128, g: 128, b: 0 });
});
it('should interpolate colors for position 0.75', () => {
const result = lerpRGBColor({ leftColor, rightColor, position: 0.75 });
expect(result).toEqual({ r: 64, g: 191, b: 0 });
});
});
type RGBColor = {
r: number;
g: number;
b: number;
};
type InterpolateColorProps = {
leftColor: RGBColor;
rightColor: RGBColor;
position: number;
};
export const lerpRGBColor = ({
leftColor,
rightColor,
position,
}: InterpolateColorProps): RGBColor => {
const result = {} as RGBColor;
Object.keys(leftColor).forEach(key => {
const numericKey = key as keyof RGBColor;
result[numericKey] = Math.round(
leftColor[numericKey] + (rightColor[numericKey] - leftColor[numericKey]) * position,
);
});
return result;
};
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment