mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-20 07:46:37 +00:00
Common-color-tests (#23258)
This commit is contained in:
parent
145a536156
commit
27ce395d68
@ -90,9 +90,9 @@ export const lab2rgb = (
|
|||||||
x = Xn * lab_xyz(x);
|
x = Xn * lab_xyz(x);
|
||||||
z = Zn * lab_xyz(z);
|
z = Zn * lab_xyz(z);
|
||||||
|
|
||||||
const r = xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z); // D65 -> sRGB
|
const r = Math.round(xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z)); // D65 -> sRGB
|
||||||
const g = xyz_rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z);
|
const g = Math.round(xyz_rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z));
|
||||||
const b_ = xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z);
|
const b_ = Math.round(xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z));
|
||||||
|
|
||||||
return [r, g, b_];
|
return [r, g, b_];
|
||||||
};
|
};
|
||||||
|
@ -8,9 +8,9 @@ export const temperature2rgb = (
|
|||||||
): [number, number, number] => {
|
): [number, number, number] => {
|
||||||
const value = temperature / 100;
|
const value = temperature / 100;
|
||||||
return [
|
return [
|
||||||
temperatureRed(value),
|
Math.round(temperatureRed(value)),
|
||||||
temperatureGreen(value),
|
Math.round(temperatureGreen(value)),
|
||||||
temperatureBlue(value),
|
Math.round(temperatureBlue(value)),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,10 +59,10 @@ const matchMaxScale = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mired2kelvin = (miredTemperature: number) =>
|
export const mired2kelvin = (miredTemperature: number) =>
|
||||||
Math.floor(1000000 / miredTemperature);
|
miredTemperature === 0 ? 1000000 : Math.floor(1000000 / miredTemperature);
|
||||||
|
|
||||||
export const kelvin2mired = (kelvintTemperature: number) =>
|
export const kelvin2mired = (kelvinTemperature: number) =>
|
||||||
Math.floor(1000000 / kelvintTemperature);
|
kelvinTemperature === 0 ? 1000000 : Math.floor(1000000 / kelvinTemperature);
|
||||||
|
|
||||||
export const rgbww2rgb = (
|
export const rgbww2rgb = (
|
||||||
rgbww: [number, number, number, number, number],
|
rgbww: [number, number, number, number, number],
|
||||||
|
@ -14,8 +14,8 @@ export const hexBlend = (c1: string, c2: string, blend = 50): string => {
|
|||||||
c1 = expandHex(c1);
|
c1 = expandHex(c1);
|
||||||
c2 = expandHex(c2);
|
c2 = expandHex(c2);
|
||||||
for (let i = 0; i <= 5; i += 2) {
|
for (let i = 0; i <= 5; i += 2) {
|
||||||
const h1 = parseInt(c1.substr(i, 2), 16);
|
const h1 = parseInt(c1.substring(i, i + 2), 16);
|
||||||
const h2 = parseInt(c2.substr(i, 2), 16);
|
const h2 = parseInt(c2.substring(i, i + 2), 16);
|
||||||
let hex = Math.floor(h2 + (h1 - h2) * (blend / 100)).toString(16);
|
let hex = Math.floor(h2 + (h1 - h2) * (blend / 100)).toString(16);
|
||||||
while (hex.length < 2) hex = "0" + hex;
|
while (hex.length < 2) hex = "0" + hex;
|
||||||
color += hex;
|
color += hex;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// From https://github.com/gka/chroma.js
|
// From https://github.com/gka/chroma.js
|
||||||
// Copyright (c) 2011-2019, Gregor Aisch
|
// Copyright (c) 2011-2019, Gregor Aisch
|
||||||
|
|
||||||
export const labDarken = (
|
export type LabColor = [number, number, number];
|
||||||
lab: [number, number, number],
|
|
||||||
amount = 1
|
|
||||||
): [number, number, number] => [lab[0] - 18 * amount, lab[1], lab[2]];
|
|
||||||
|
|
||||||
export const labBrighten = (
|
export const labDarken = (lab: LabColor, amount = 1): LabColor => [
|
||||||
lab: [number, number, number],
|
lab[0] - 18 * amount,
|
||||||
amount = 1
|
lab[1],
|
||||||
): [number, number, number] => labDarken(lab, -amount);
|
lab[2],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const labBrighten = (lab: LabColor, amount = 1): LabColor =>
|
||||||
|
labDarken(lab, -amount);
|
||||||
|
35
test/common/color/colors.test.ts
Normal file
35
test/common/color/colors.test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { describe, test, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
getColorByIndex,
|
||||||
|
getGraphColorByIndex,
|
||||||
|
COLORS,
|
||||||
|
} from "../../../src/common/color/colors";
|
||||||
|
import { theme2hex } from "../../../src/common/color/convert-color";
|
||||||
|
|
||||||
|
describe("getColorByIndex", () => {
|
||||||
|
test("return the correct color for a given index", () => {
|
||||||
|
expect(getColorByIndex(0)).toBe(COLORS[0]);
|
||||||
|
expect(getColorByIndex(10)).toBe(COLORS[10]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("wrap around if the index is greater than the length of COLORS", () => {
|
||||||
|
expect(getColorByIndex(COLORS.length)).toBe(COLORS[0]);
|
||||||
|
expect(getColorByIndex(COLORS.length + 4)).toBe(COLORS[4]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getGraphColorByIndex", () => {
|
||||||
|
test("return the correct theme color if it exists", () => {
|
||||||
|
const style = {
|
||||||
|
getPropertyValue: (prop) => (prop === "--graph-color-1" ? "#123456" : ""),
|
||||||
|
} as CSSStyleDeclaration;
|
||||||
|
expect(getGraphColorByIndex(0, style)).toBe(theme2hex("#123456"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("return the default color if the theme color does not exist", () => {
|
||||||
|
const style = {
|
||||||
|
getPropertyValue: () => "",
|
||||||
|
} as unknown as CSSStyleDeclaration;
|
||||||
|
expect(getGraphColorByIndex(0, style)).toBe(theme2hex(COLORS[0]));
|
||||||
|
});
|
||||||
|
});
|
18
test/common/color/compute-color.test.ts
Normal file
18
test/common/color/compute-color.test.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
computeCssColor,
|
||||||
|
THEME_COLORS,
|
||||||
|
} from "../../../src/common/color/compute-color";
|
||||||
|
|
||||||
|
describe("computeCssColor", () => {
|
||||||
|
it("should return CSS variable for theme colors", () => {
|
||||||
|
THEME_COLORS.forEach((color) => {
|
||||||
|
expect(computeCssColor(color)).toBe(`var(--${color}-color)`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the input color if it is not a theme color", () => {
|
||||||
|
const nonThemeColor = "non-theme-color";
|
||||||
|
expect(computeCssColor(nonThemeColor)).toBe(nonThemeColor);
|
||||||
|
});
|
||||||
|
});
|
54
test/common/color/convert-color.test.ts
Normal file
54
test/common/color/convert-color.test.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
hex2rgb,
|
||||||
|
rgb2hex,
|
||||||
|
rgb2lab,
|
||||||
|
lab2rgb,
|
||||||
|
lab2hex,
|
||||||
|
rgb2hsv,
|
||||||
|
hsv2rgb,
|
||||||
|
rgb2hs,
|
||||||
|
hs2rgb,
|
||||||
|
theme2hex,
|
||||||
|
} from "../../../src/common/color/convert-color";
|
||||||
|
|
||||||
|
describe("Color Conversion Tests", () => {
|
||||||
|
it("should convert hex to rgb", () => {
|
||||||
|
expect(hex2rgb("#ffffff")).toEqual([255, 255, 255]);
|
||||||
|
expect(hex2rgb("#000000")).toEqual([0, 0, 0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert rgb to hex", () => {
|
||||||
|
expect(rgb2hex([255, 255, 255])).toBe("#ffffff");
|
||||||
|
expect(rgb2hex([0, 0, 0])).toBe("#000000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert rgb to lab and back", () => {
|
||||||
|
const rgb: [number, number, number] = [12, 206, 7];
|
||||||
|
const lab = rgb2lab(rgb);
|
||||||
|
expect(lab2rgb(lab)).toEqual(rgb);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert lab to hex", () => {
|
||||||
|
const lab: [number, number, number] = [53.23288, 80.10933, 67.22006];
|
||||||
|
expect(lab2hex(lab)).toBe("#ff0000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert rgb to hsv and back", () => {
|
||||||
|
const rgb: [number, number, number] = [255, 0, 0];
|
||||||
|
const hsv = rgb2hsv(rgb);
|
||||||
|
expect(hsv2rgb(hsv)).toEqual(rgb);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert rgb to hs and back", () => {
|
||||||
|
const rgb: [number, number, number] = [255, 0, 0];
|
||||||
|
const hs = rgb2hs(rgb);
|
||||||
|
expect(hs2rgb(hs)).toEqual([255, 0, 0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert theme color to hex", () => {
|
||||||
|
expect(theme2hex("red")).toBe("#ff0000");
|
||||||
|
expect(theme2hex("#ff0000")).toBe("#ff0000");
|
||||||
|
expect(theme2hex("unicorn")).toBe("unicorn");
|
||||||
|
});
|
||||||
|
});
|
50
test/common/color/convert-light-color.test.ts
Normal file
50
test/common/color/convert-light-color.test.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
temperature2rgb,
|
||||||
|
mired2kelvin,
|
||||||
|
kelvin2mired,
|
||||||
|
rgbww2rgb,
|
||||||
|
rgbw2rgb,
|
||||||
|
} from "../../../src/common/color/convert-light-color";
|
||||||
|
|
||||||
|
describe("temperature2rgb", () => {
|
||||||
|
it("should convert temperature to RGB", () => {
|
||||||
|
expect(temperature2rgb(6600)).toEqual([255, 255, 255]);
|
||||||
|
expect(temperature2rgb(6601)).toEqual([255, 252, 255]);
|
||||||
|
expect(temperature2rgb(1900)).toEqual([255, 132, 0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("mired2kelvin", () => {
|
||||||
|
it("should convert mired to kelvin", () => {
|
||||||
|
expect(mired2kelvin(20)).toBe(50000);
|
||||||
|
expect(mired2kelvin(0)).toBe(1000000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("kelvin2mired", () => {
|
||||||
|
it("should convert kelvin to mired", () => {
|
||||||
|
expect(kelvin2mired(6500)).toBe(153);
|
||||||
|
expect(kelvin2mired(2700)).toBe(370);
|
||||||
|
expect(kelvin2mired(0)).toBe(1000000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("rgbww2rgb", () => {
|
||||||
|
it("should convert RGBWW to RGB", () => {
|
||||||
|
expect(rgbww2rgb([255, 0, 0, 255, 0])).toEqual([255, 128, 126]);
|
||||||
|
expect(rgbww2rgb([0, 255, 0, 0, 255])).toEqual([154, 255, 53]);
|
||||||
|
expect(rgbww2rgb([255, 0, 0, 255, 128], 1000)).toEqual([255, 75, 25]);
|
||||||
|
expect(rgbww2rgb([255, 0, 0, 255, 128], undefined, 5000)).toEqual([
|
||||||
|
255, 102, 81,
|
||||||
|
]);
|
||||||
|
expect(rgbww2rgb([255, 0, 0, 255, 128], 3000, 4000)).toEqual([255, 98, 73]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("rgbw2rgb", () => {
|
||||||
|
it("should convert RGBW to RGB", () => {
|
||||||
|
expect(rgbw2rgb([255, 0, 0, 255])).toEqual([255, 128, 128]);
|
||||||
|
expect(rgbw2rgb([0, 255, 0, 0])).toEqual([0, 255, 0]);
|
||||||
|
});
|
||||||
|
});
|
30
test/common/color/hex.test.ts
Normal file
30
test/common/color/hex.test.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { expandHex, hexBlend } from "../../../src/common/color/hex";
|
||||||
|
|
||||||
|
describe("expandHex", () => {
|
||||||
|
it("should expand a 3-digit hex code to 6 digits", () => {
|
||||||
|
expect(expandHex("#abc")).toBe("aabbcc");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return a 6-digit hex code unchanged", () => {
|
||||||
|
expect(expandHex("#abcdef")).toBe("abcdef");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hexBlend", () => {
|
||||||
|
it("should blend two hex colors with default blend value", () => {
|
||||||
|
expect(hexBlend("#000000", "#ffffff")).toBe("#7f7f7f");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should blend two hex colors with a specified blend value", () => {
|
||||||
|
expect(hexBlend("#ff0000", "#0000ff", 25)).toBe("#3f00bf");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the first color if blend is 100", () => {
|
||||||
|
expect(hexBlend("#ff0000", "#0000ff", 100)).toBe("#ff0000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the second color if blend is 0", () => {
|
||||||
|
expect(hexBlend("#ff0000", "#0000ff", 0)).toBe("#0000ff");
|
||||||
|
});
|
||||||
|
});
|
34
test/common/color/lab.test.ts
Normal file
34
test/common/color/lab.test.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
labDarken,
|
||||||
|
labBrighten,
|
||||||
|
type LabColor,
|
||||||
|
} from "../../../src/common/color/lab";
|
||||||
|
|
||||||
|
describe("labDarken", () => {
|
||||||
|
it("should darken the color by the default amount", () => {
|
||||||
|
const lab: LabColor = [50, 20, 30];
|
||||||
|
const result = labDarken(lab);
|
||||||
|
expect(result).toEqual([32, 20, 30]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should darken the color by a specified amount", () => {
|
||||||
|
const lab: LabColor = [50, 20, 30];
|
||||||
|
const result = labDarken(lab, 2);
|
||||||
|
expect(result).toEqual([14, 20, 30]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("labBrighten", () => {
|
||||||
|
it("should brighten the color by the default amount", () => {
|
||||||
|
const lab: LabColor = [50, 20, 30];
|
||||||
|
const result = labBrighten(lab);
|
||||||
|
expect(result).toEqual([68, 20, 30]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should brighten the color by a specified amount", () => {
|
||||||
|
const lab: LabColor = [50, 20, 30];
|
||||||
|
const result = labBrighten(lab, 2);
|
||||||
|
expect(result).toEqual([86, 20, 30]);
|
||||||
|
});
|
||||||
|
});
|
41
test/common/color/rgb.test.ts
Normal file
41
test/common/color/rgb.test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import {
|
||||||
|
luminosity,
|
||||||
|
rgbContrast,
|
||||||
|
getRGBContrastRatio,
|
||||||
|
} from "../../../src/common/color/rgb";
|
||||||
|
|
||||||
|
describe("luminosity", () => {
|
||||||
|
it("calculates the correct luminosity for black", () => {
|
||||||
|
expect(luminosity([0, 0, 0])).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calculates the correct luminosity for white", () => {
|
||||||
|
expect(luminosity([255, 255, 255])).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calculates the correct luminosity for red", () => {
|
||||||
|
expect(luminosity([255, 0, 0])).toBe(0.2126);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("rgbContrast", () => {
|
||||||
|
it("calculates the correct contrast ratio between black and white", () => {
|
||||||
|
expect(rgbContrast([0, 0, 0], [255, 255, 255])).toBe(21);
|
||||||
|
expect(rgbContrast([255, 255, 255], [0, 0, 0])).toBe(21);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calculates the correct contrast ratio between red and white", () => {
|
||||||
|
expect(rgbContrast([255, 0, 0], [255, 255, 255])).toBeCloseTo(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getRGBContrastRatio", () => {
|
||||||
|
it("calculates the correct rounded contrast ratio between black and white", () => {
|
||||||
|
expect(getRGBContrastRatio([0, 0, 0], [255, 255, 255])).toBe(21);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calculates the correct rounded contrast ratio between red and white", () => {
|
||||||
|
expect(getRGBContrastRatio([255, 0, 0], [255, 255, 255])).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user