From d81d772cdfb7c58b6ff7677ab0140ad4b6f69820 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Sat, 11 Mar 2023 09:10:56 +1100 Subject: [PATCH] feat(lib): add more tests to lib package (#7210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(lib): add more tests to lib package Add more tests to the lib package to make it more robust overall. Additionally, tidy any methods that can be modified without changing behaviour and tighten types where possible. * fix(lib): update missed imports * fix: revert stylistic changes * Update getSchedule.test.ts --------- Co-authored-by: Omar López --- apps/web/pages/api/user/avatar.ts | 2 +- apps/web/pages/team/[slug].tsx | 2 +- apps/web/test/lib/getSchedule.test.ts | 1 + jest.config.ts | 6 + .../teams/components/TeamInviteListItem.tsx | 4 +- .../ee/teams/components/TeamListItem.tsx | 5 +- .../features/ee/teams/pages/availability.tsx | 2 +- .../ee/teams/pages/team-profile-view.tsx | 2 +- packages/lib/CustomBranding.test.tsx | 155 ++++++++ packages/lib/CustomBranding.tsx | 363 ++++++++++-------- packages/lib/default-cookies.ts | 1 + packages/lib/defaultAvatarImage.test.ts | 65 ++++ packages/lib/defaultAvatarImage.ts | 13 + packages/lib/getPlaceholderAvatar.tsx | 6 - packages/lib/isBookingLimits.ts | 1 + packages/lib/isRecurringEvent.ts | 7 +- packages/lib/profile.ts | 11 - packages/lib/random.test.ts | 30 ++ packages/lib/random.ts | 12 +- packages/lib/text.test.ts | 70 ++++ packages/lib/text.ts | 3 + packages/lib/timeFormat.ts | 9 + packages/lib/tsconfig.json | 1 + packages/lib/tsconfig.test.json | 13 + packages/lib/webstorage.ts | 6 +- packages/lib/weekday.test.ts | 64 +++ packages/lib/weekday.ts | 12 +- packages/trpc/server/createContext.ts | 2 +- 28 files changed, 675 insertions(+), 193 deletions(-) create mode 100644 packages/lib/CustomBranding.test.tsx create mode 100644 packages/lib/defaultAvatarImage.test.ts delete mode 100644 packages/lib/getPlaceholderAvatar.tsx delete mode 100644 packages/lib/profile.ts create mode 100644 packages/lib/random.test.ts create mode 100644 packages/lib/text.test.ts create mode 100644 packages/lib/tsconfig.test.json create mode 100644 packages/lib/weekday.test.ts diff --git a/apps/web/pages/api/user/avatar.ts b/apps/web/pages/api/user/avatar.ts index 6a99a802c6..27b171c2da 100644 --- a/apps/web/pages/api/user/avatar.ts +++ b/apps/web/pages/api/user/avatar.ts @@ -2,7 +2,7 @@ import crypto from "crypto"; import type { NextApiRequest, NextApiResponse } from "next"; import { z } from "zod"; -import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar"; +import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import prisma from "@calcom/prisma"; import { defaultAvatarSrc } from "@lib/profile"; diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx index fc4986ceb9..057f996b26 100644 --- a/apps/web/pages/team/[slug].tsx +++ b/apps/web/pages/team/[slug].tsx @@ -7,7 +7,7 @@ import { useEffect } from "react"; import { useIsEmbed } from "@calcom/embed-core/embed-iframe"; import EventTypeDescription from "@calcom/features/eventtypes/components/EventTypeDescription"; import { CAL_URL } from "@calcom/lib/constants"; -import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar"; +import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useTheme from "@calcom/lib/hooks/useTheme"; import { md } from "@calcom/lib/markdownIt"; diff --git a/apps/web/test/lib/getSchedule.test.ts b/apps/web/test/lib/getSchedule.test.ts index 440563ef47..a99c48fc2d 100644 --- a/apps/web/test/lib/getSchedule.test.ts +++ b/apps/web/test/lib/getSchedule.test.ts @@ -504,6 +504,7 @@ describe("getSchedule", () => { ); }); + // FIXME: Fix minimumBookingNotice is respected test test.skip("minimumBookingNotice is respected", async () => { jest.useFakeTimers().setSystemTime( (() => { diff --git a/jest.config.ts b/jest.config.ts index 31b6064651..a7b3448f64 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -35,9 +35,15 @@ const config: Config = { displayName: "@calcom/lib", roots: ["/packages/lib"], testEnvironment: "node", + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], transform: { "^.+\\.tsx?$": "ts-jest", }, + globals: { + "ts-jest": { + tsconfig: "/packages/lib/tsconfig.test.json", + }, + }, }, { displayName: "@calcom/closecom", diff --git a/packages/features/ee/teams/components/TeamInviteListItem.tsx b/packages/features/ee/teams/components/TeamInviteListItem.tsx index 96c4e79671..17b74831f0 100644 --- a/packages/features/ee/teams/components/TeamInviteListItem.tsx +++ b/packages/features/ee/teams/components/TeamInviteListItem.tsx @@ -1,7 +1,7 @@ -import { MembershipRole } from "@prisma/client"; +import type { MembershipRole } from "@prisma/client"; import classNames from "@calcom/lib/classNames"; -import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar"; +import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { diff --git a/packages/features/ee/teams/components/TeamListItem.tsx b/packages/features/ee/teams/components/TeamListItem.tsx index a237d72581..6ef0d2ae4a 100644 --- a/packages/features/ee/teams/components/TeamListItem.tsx +++ b/packages/features/ee/teams/components/TeamListItem.tsx @@ -5,9 +5,10 @@ import { useState } from "react"; import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal"; import classNames from "@calcom/lib/classNames"; -import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar"; +import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { RouterOutputs, trpc } from "@calcom/trpc/react"; +import type { RouterOutputs } from "@calcom/trpc/react"; +import { trpc } from "@calcom/trpc/react"; import { Avatar, Button, diff --git a/packages/features/ee/teams/pages/availability.tsx b/packages/features/ee/teams/pages/availability.tsx index 41164f41e7..500d7d4e05 100644 --- a/packages/features/ee/teams/pages/availability.tsx +++ b/packages/features/ee/teams/pages/availability.tsx @@ -1,7 +1,7 @@ import { useRouter } from "next/router"; import { useMemo, useState } from "react"; -import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar"; +import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { trpc } from "@calcom/trpc/react"; import { Alert, Avatar, Loader, Shell } from "@calcom/ui"; diff --git a/packages/features/ee/teams/pages/team-profile-view.tsx b/packages/features/ee/teams/pages/team-profile-view.tsx index 34e7530037..419aec51a0 100644 --- a/packages/features/ee/teams/pages/team-profile-view.tsx +++ b/packages/features/ee/teams/pages/team-profile-view.tsx @@ -8,7 +8,7 @@ import { Controller, useForm } from "react-hook-form"; import { z } from "zod"; import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; -import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar"; +import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { md } from "@calcom/lib/markdownIt"; import objectKeys from "@calcom/lib/objectKeys"; diff --git a/packages/lib/CustomBranding.test.tsx b/packages/lib/CustomBranding.test.tsx new file mode 100644 index 0000000000..5affe88bb1 --- /dev/null +++ b/packages/lib/CustomBranding.test.tsx @@ -0,0 +1,155 @@ +import { colorNameToHex, fallBackHex, isValidHexCode } from "./CustomBranding"; + +describe("Custom Branding tests", () => { + describe("fn: colorNameToHex", () => { + it("should return a hex color when a valid color name is provided", () => { + const cases = [ + { + input: "red", + expected: "#ff0000", + }, + { + input: "green", + expected: "#008000", + }, + { + input: "salmon", + expected: "#fa8072", + }, + { + input: "rebeccapurple", + expected: "#663399", + }, + ]; + + for (const { input, expected } of cases) { + const result = colorNameToHex(input); + + expect(result).toEqual(expected); + } + }); + + it("should return false when an invalid color name is provided", () => { + const result = colorNameToHex("invalid"); + + expect(result).toEqual(false); + }); + }); + + describe("fn: isValidHexCode", () => { + it("should return true when a valid hex code is provided", () => { + const cases = [ + { + input: "#ff0000", + expected: true, + }, + { + input: "#00Ff00", + expected: true, + }, + { + input: "#fA8072", + expected: true, + }, + { + input: "#663", + expected: true, + }, + { + input: "#fAb", + expected: true, + }, + { + input: "#F00F00", + expected: true, + }, + ]; + + for (const { input, expected } of cases) { + const result = isValidHexCode(input); + + if (!result) { + console.log("input", input); + } + + expect(result).toEqual(expected); + } + }); + + it("should return false when an invalid hex code is provided", () => { + const cases = [ + { + input: "#ff000", + expected: false, + }, + { + input: "#F000G0", + expected: false, + }, + { + input: "#00ff00a", + expected: false, + }, + { + input: "#fa8072aa", + expected: false, + }, + { + input: "#663399aa", + expected: false, + }, + ]; + + for (const { input, expected } of cases) { + const result = isValidHexCode(input); + + expect(result).toEqual(expected); + } + }); + }); + + describe("fn: fallBackHex", () => { + it("should return a hex color when a valid color name is provided", () => { + const cases = [ + { + input: "red", + expected: "#ff0000", + }, + { + input: "green", + expected: "#008000", + }, + { + input: "salmon", + expected: "#fa8072", + }, + { + input: "rebeccapurple", + expected: "#663399", + }, + ]; + + for (const { input, expected } of cases) { + const result = colorNameToHex(input); + + expect(result).toEqual(expected); + } + }); + + it("should return a brand color when there is no hex fallback", () => { + // BRAND_COLOR => "#292929" + // BRAND_TEXT_COLOR => "#ffffff" + // DARK_BRAND_COLOR => "#fafafa" + + const inputs = ["reddit", null, "darkbruwn"]; + + for (const input of inputs) { + const resultLight = fallBackHex(input, false); + const resultDark = fallBackHex(input, true); + + expect(resultLight).toEqual("#292929"); + expect(resultDark).toEqual("#fafafa"); + } + }); + }); +}); diff --git a/packages/lib/CustomBranding.tsx b/packages/lib/CustomBranding.tsx index 8512de4de0..4bf695a8bd 100644 --- a/packages/lib/CustomBranding.tsx +++ b/packages/lib/CustomBranding.tsx @@ -2,165 +2,178 @@ import Head from "next/head"; import { useBrandColors } from "@calcom/embed-core/embed-iframe"; -const brandColor = "#292929"; -const brandTextColor = "#ffffff"; -const darkBrandColor = "#fafafa"; +const BRAND_COLOR = "#292929"; +const BRAND_TEXT_COLOR = "#ffffff"; +const DARK_BRAND_COLOR = "#fafafa"; + +const HTML_COLORS = { + aliceblue: "#f0f8ff", + antiquewhite: "#faebd7", + aqua: "#00ffff", + aquamarine: "#7fffd4", + azure: "#f0ffff", + beige: "#f5f5dc", + bisque: "#ffe4c4", + black: "#000000", + blanchedalmond: "#ffebcd", + blue: "#0000ff", + blueviolet: "#8a2be2", + brown: "#a52a2a", + burlywood: "#deb887", + cadetblue: "#5f9ea0", + chartreuse: "#7fff00", + chocolate: "#d2691e", + coral: "#ff7f50", + cornflowerblue: "#6495ed", + cornsilk: "#fff8dc", + crimson: "#dc143c", + cyan: "#00ffff", + darkblue: "#00008b", + darkcyan: "#008b8b", + darkgoldenrod: "#b8860b", + darkgray: "#a9a9a9", + darkgreen: "#006400", + darkkhaki: "#bdb76b", + darkmagenta: "#8b008b", + darkolivegreen: "#556b2f", + darkorange: "#ff8c00", + darkorchid: "#9932cc", + darkred: "#8b0000", + darksalmon: "#e9967a", + darkseagreen: "#8fbc8f", + darkslateblue: "#483d8b", + darkslategray: "#2f4f4f", + darkturquoise: "#00ced1", + darkviolet: "#9400d3", + deeppink: "#ff1493", + deepskyblue: "#00bfff", + dimgray: "#696969", + dodgerblue: "#1e90ff", + firebrick: "#b22222", + floralwhite: "#fffaf0", + forestgreen: "#228b22", + fuchsia: "#ff00ff", + gainsboro: "#dcdcdc", + ghostwhite: "#f8f8ff", + gold: "#ffd700", + goldenrod: "#daa520", + gray: "#808080", + green: "#008000", + greenyellow: "#adff2f", + honeydew: "#f0fff0", + hotpink: "#ff69b4", + "indianred ": "#cd5c5c", + indigo: "#4b0082", + ivory: "#fffff0", + khaki: "#f0e68c", + lavender: "#e6e6fa", + lavenderblush: "#fff0f5", + lawngreen: "#7cfc00", + lemonchiffon: "#fffacd", + lightblue: "#add8e6", + lightcoral: "#f08080", + lightcyan: "#e0ffff", + lightgoldenrodyellow: "#fafad2", + lightgrey: "#d3d3d3", + lightgreen: "#90ee90", + lightpink: "#ffb6c1", + lightsalmon: "#ffa07a", + lightseagreen: "#20b2aa", + lightskyblue: "#87cefa", + lightslategray: "#778899", + lightsteelblue: "#b0c4de", + lightyellow: "#ffffe0", + lime: "#00ff00", + limegreen: "#32cd32", + linen: "#faf0e6", + magenta: "#ff00ff", + maroon: "#800000", + mediumaquamarine: "#66cdaa", + mediumblue: "#0000cd", + mediumorchid: "#ba55d3", + mediumpurple: "#9370d8", + mediumseagreen: "#3cb371", + mediumslateblue: "#7b68ee", + mediumspringgreen: "#00fa9a", + mediumturquoise: "#48d1cc", + mediumvioletred: "#c71585", + midnightblue: "#191970", + mintcream: "#f5fffa", + mistyrose: "#ffe4e1", + moccasin: "#ffe4b5", + navajowhite: "#ffdead", + navy: "#000080", + oldlace: "#fdf5e6", + olive: "#808000", + olivedrab: "#6b8e23", + orange: "#ffa500", + orangered: "#ff4500", + orchid: "#da70d6", + palegoldenrod: "#eee8aa", + palegreen: "#98fb98", + paleturquoise: "#afeeee", + palevioletred: "#d87093", + papayawhip: "#ffefd5", + peachpuff: "#ffdab9", + peru: "#cd853f", + pink: "#ffc0cb", + plum: "#dda0dd", + powderblue: "#b0e0e6", + purple: "#800080", + rebeccapurple: "#663399", + red: "#ff0000", + rosybrown: "#bc8f8f", + royalblue: "#4169e1", + saddlebrown: "#8b4513", + salmon: "#fa8072", + sandybrown: "#f4a460", + seagreen: "#2e8b57", + seashell: "#fff5ee", + sienna: "#a0522d", + silver: "#c0c0c0", + skyblue: "#87ceeb", + slateblue: "#6a5acd", + slategray: "#708090", + snow: "#fffafa", + springgreen: "#00ff7f", + steelblue: "#4682b4", + tan: "#d2b48c", + teal: "#008080", + thistle: "#d8bfd8", + tomato: "#ff6347", + turquoise: "#40e0d0", + violet: "#ee82ee", + wheat: "#f5deb3", + white: "#ffffff", + whitesmoke: "#f5f5f5", + yellow: "#ffff00", + yellowgreen: "#9acd32", +}; + +// Shadow the HTML_COLORS constant so we can create a type guard for it. +type HTML_COLORS = typeof HTML_COLORS; + +function isHtmlColor(color: string): color is keyof HTML_COLORS { + return color in HTML_COLORS; +} export function colorNameToHex(color: string) { - const colors = { - aliceblue: "#f0f8ff", - antiquewhite: "#faebd7", - aqua: "#00ffff", - aquamarine: "#7fffd4", - azure: "#f0ffff", - beige: "#f5f5dc", - bisque: "#ffe4c4", - black: "#000000", - blanchedalmond: "#ffebcd", - blue: "#0000ff", - blueviolet: "#8a2be2", - brown: "#a52a2a", - burlywood: "#deb887", - cadetblue: "#5f9ea0", - chartreuse: "#7fff00", - chocolate: "#d2691e", - coral: "#ff7f50", - cornflowerblue: "#6495ed", - cornsilk: "#fff8dc", - crimson: "#dc143c", - cyan: "#00ffff", - darkblue: "#00008b", - darkcyan: "#008b8b", - darkgoldenrod: "#b8860b", - darkgray: "#a9a9a9", - darkgreen: "#006400", - darkkhaki: "#bdb76b", - darkmagenta: "#8b008b", - darkolivegreen: "#556b2f", - darkorange: "#ff8c00", - darkorchid: "#9932cc", - darkred: "#8b0000", - darksalmon: "#e9967a", - darkseagreen: "#8fbc8f", - darkslateblue: "#483d8b", - darkslategray: "#2f4f4f", - darkturquoise: "#00ced1", - darkviolet: "#9400d3", - deeppink: "#ff1493", - deepskyblue: "#00bfff", - dimgray: "#696969", - dodgerblue: "#1e90ff", - firebrick: "#b22222", - floralwhite: "#fffaf0", - forestgreen: "#228b22", - fuchsia: "#ff00ff", - gainsboro: "#dcdcdc", - ghostwhite: "#f8f8ff", - gold: "#ffd700", - goldenrod: "#daa520", - gray: "#808080", - green: "#008000", - greenyellow: "#adff2f", - honeydew: "#f0fff0", - hotpink: "#ff69b4", - "indianred ": "#cd5c5c", - indigo: "#4b0082", - ivory: "#fffff0", - khaki: "#f0e68c", - lavender: "#e6e6fa", - lavenderblush: "#fff0f5", - lawngreen: "#7cfc00", - lemonchiffon: "#fffacd", - lightblue: "#add8e6", - lightcoral: "#f08080", - lightcyan: "#e0ffff", - lightgoldenrodyellow: "#fafad2", - lightgrey: "#d3d3d3", - lightgreen: "#90ee90", - lightpink: "#ffb6c1", - lightsalmon: "#ffa07a", - lightseagreen: "#20b2aa", - lightskyblue: "#87cefa", - lightslategray: "#778899", - lightsteelblue: "#b0c4de", - lightyellow: "#ffffe0", - lime: "#00ff00", - limegreen: "#32cd32", - linen: "#faf0e6", - magenta: "#ff00ff", - maroon: "#800000", - mediumaquamarine: "#66cdaa", - mediumblue: "#0000cd", - mediumorchid: "#ba55d3", - mediumpurple: "#9370d8", - mediumseagreen: "#3cb371", - mediumslateblue: "#7b68ee", - mediumspringgreen: "#00fa9a", - mediumturquoise: "#48d1cc", - mediumvioletred: "#c71585", - midnightblue: "#191970", - mintcream: "#f5fffa", - mistyrose: "#ffe4e1", - moccasin: "#ffe4b5", - navajowhite: "#ffdead", - navy: "#000080", - oldlace: "#fdf5e6", - olive: "#808000", - olivedrab: "#6b8e23", - orange: "#ffa500", - orangered: "#ff4500", - orchid: "#da70d6", - palegoldenrod: "#eee8aa", - palegreen: "#98fb98", - paleturquoise: "#afeeee", - palevioletred: "#d87093", - papayawhip: "#ffefd5", - peachpuff: "#ffdab9", - peru: "#cd853f", - pink: "#ffc0cb", - plum: "#dda0dd", - powderblue: "#b0e0e6", - purple: "#800080", - rebeccapurple: "#663399", - red: "#ff0000", - rosybrown: "#bc8f8f", - royalblue: "#4169e1", - saddlebrown: "#8b4513", - salmon: "#fa8072", - sandybrown: "#f4a460", - seagreen: "#2e8b57", - seashell: "#fff5ee", - sienna: "#a0522d", - silver: "#c0c0c0", - skyblue: "#87ceeb", - slateblue: "#6a5acd", - slategray: "#708090", - snow: "#fffafa", - springgreen: "#00ff7f", - steelblue: "#4682b4", - tan: "#d2b48c", - teal: "#008080", - thistle: "#d8bfd8", - tomato: "#ff6347", - turquoise: "#40e0d0", - violet: "#ee82ee", - wheat: "#f5deb3", - white: "#ffffff", - whitesmoke: "#f5f5f5", - yellow: "#ffff00", - yellowgreen: "#9acd32", - }; + const normalizedColor = color.toLowerCase(); - return colors[color.toLowerCase() as keyof typeof colors] !== undefined - ? colors[color.toLowerCase() as keyof typeof colors] - : false; + if (isHtmlColor(normalizedColor)) { + return HTML_COLORS[normalizedColor]; + } + + return false; } function computeContrastRatio(a: number[], b: number[]) { const lum1 = computeLuminance(a[0], a[1], a[2]); const lum2 = computeLuminance(b[0], b[1], b[2]); + const brightest = Math.max(lum1, lum2); const darkest = Math.min(lum1, lum2); + return (brightest + 0.05) / (darkest + 0.05); } @@ -169,19 +182,25 @@ function computeLuminance(r: number, g: number, b: number) { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); }); + return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; } function hexToRGB(hex: string) { const color = hex.replace("#", ""); + return [parseInt(color.slice(0, 2), 16), parseInt(color.slice(2, 4), 16), parseInt(color.slice(4, 6), 16)]; } function normalizeHexCode(hex: string | null, dark: boolean) { if (!hex) { - return !dark ? brandColor : darkBrandColor; + return !dark ? BRAND_COLOR : DARK_BRAND_COLOR; } + hex = hex.replace("#", ""); + + // If the length of the hex code is 3, double up each character + // e.g. fff => ffffff or a0e => aa00ee if (hex.length === 3) { hex = hex .split("") @@ -190,54 +209,84 @@ function normalizeHexCode(hex: string | null, dark: boolean) { }) .join(""); } + return hex; } function getContrastingTextColor(bgColor: string | null, dark: boolean): string { - bgColor = bgColor == "" || bgColor == null ? (dark ? darkBrandColor : brandColor) : bgColor; + bgColor = bgColor == "" || bgColor == null ? (dark ? DARK_BRAND_COLOR : BRAND_COLOR) : bgColor; + const rgb = hexToRGB(bgColor); + const whiteContrastRatio = computeContrastRatio(rgb, [255, 255, 255]); const blackContrastRatio = computeContrastRatio(rgb, [41, 41, 41]); //#292929 - return whiteContrastRatio > blackContrastRatio ? brandTextColor : brandColor; + + return whiteContrastRatio > blackContrastRatio ? BRAND_TEXT_COLOR : BRAND_COLOR; } +/** + * Given a string, determine if it's a valid 6 character hex code. + */ export function isValidHexCode(val: string | null) { if (val) { + // Normalize the value into include a leading "#" if it's missing val = val.indexOf("#") === 0 ? val : "#" + val; - const regex = new RegExp("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"); + + const regex = /^#([A-F0-9]{6}|[A-F0-9]{3})$/i; + return regex.test(val); } + return false; } +/** + * Given a html color name, check if it exists in our color palette + * and if it does, return the hex code for that color. Otherwise, + * return the default brand color. + */ export function fallBackHex(val: string | null, dark: boolean): string { - if (val) if (colorNameToHex(val)) return colorNameToHex(val) as string; - return dark ? darkBrandColor : brandColor; + if (val && colorNameToHex(val)) { + return colorNameToHex(val) as string; + } + + // Otherwise, return the default color + return dark ? DARK_BRAND_COLOR : BRAND_COLOR; } +/** + * Given a light and dark brand color value, update the css variables + * within the document to reflect the new brand colors. + */ const BrandColor = ({ - lightVal = brandColor, - darkVal = darkBrandColor, + lightVal = BRAND_COLOR, + darkVal = DARK_BRAND_COLOR, }: { lightVal: string | undefined | null; darkVal: string | undefined | null; }) => { const embedBrandingColors = useBrandColors(); + lightVal = embedBrandingColors.brandColor || lightVal; + // convert to 6 digit equivalent if 3 digit code is entered lightVal = normalizeHexCode(lightVal, false); + darkVal = normalizeHexCode(darkVal, true); + // ensure acceptable hex-code lightVal = isValidHexCode(lightVal) ? lightVal?.indexOf("#") === 0 ? lightVal : "#" + lightVal : fallBackHex(lightVal, false); + darkVal = isValidHexCode(darkVal) ? darkVal?.indexOf("#") === 0 ? darkVal : "#" + darkVal : fallBackHex(darkVal, true); + return (