diff --git a/packages/visual-editor/locales/platform/cs/visual-editor.json b/packages/visual-editor/locales/platform/cs/visual-editor.json index 478378219b..a780488b92 100644 --- a/packages/visual-editor/locales/platform/cs/visual-editor.json +++ b/packages/visual-editor/locales/platform/cs/visual-editor.json @@ -275,6 +275,7 @@ "key": "Klíč", "keywordsDisplayName": "Zobrazovaný název klíčových slov", "label": "Označení", + "labelColor": "Barva označení", "latitude": "Zeměpisná šířka", "limit": "Omezit", "link": "Odkaz", diff --git a/packages/visual-editor/locales/platform/da/visual-editor.json b/packages/visual-editor/locales/platform/da/visual-editor.json index 730e6d100a..5a83e239b3 100644 --- a/packages/visual-editor/locales/platform/da/visual-editor.json +++ b/packages/visual-editor/locales/platform/da/visual-editor.json @@ -275,6 +275,7 @@ "key": "Nøgle", "keywordsDisplayName": "Søgeord Vist navn", "label": "Mærke", + "labelColor": "Mærkefarve", "latitude": "Breddegrad", "limit": "Begrænse", "link": "Forbindelse", diff --git a/packages/visual-editor/locales/platform/de/visual-editor.json b/packages/visual-editor/locales/platform/de/visual-editor.json index c37425bef4..ab91a051e0 100644 --- a/packages/visual-editor/locales/platform/de/visual-editor.json +++ b/packages/visual-editor/locales/platform/de/visual-editor.json @@ -275,6 +275,7 @@ "key": "Schlüssel", "keywordsDisplayName": "Anzeigename der Schlüsselwörter", "label": "Bezeichnung", + "labelColor": "Bezeichnungsfarbe", "latitude": "Breitengrad", "limit": "Limit", "link": "Link", diff --git a/packages/visual-editor/locales/platform/en-GB/visual-editor.json b/packages/visual-editor/locales/platform/en-GB/visual-editor.json index b0cc9408b9..4d5f942e48 100644 --- a/packages/visual-editor/locales/platform/en-GB/visual-editor.json +++ b/packages/visual-editor/locales/platform/en-GB/visual-editor.json @@ -275,6 +275,7 @@ "key": "Key", "keywordsDisplayName": "Keywords Display Name", "label": "Label", + "labelColor": "Label Color", "latitude": "Latitude", "limit": "Limit", "link": "Link", diff --git a/packages/visual-editor/locales/platform/en/visual-editor.json b/packages/visual-editor/locales/platform/en/visual-editor.json index 9cc235ee4f..4c3cc83297 100644 --- a/packages/visual-editor/locales/platform/en/visual-editor.json +++ b/packages/visual-editor/locales/platform/en/visual-editor.json @@ -275,6 +275,7 @@ "key": "Key", "keywordsDisplayName": "Keywords Display Name", "label": "Label", + "labelColor": "Label Color", "latitude": "Latitude", "limit": "Limit", "link": "Link", diff --git a/packages/visual-editor/locales/platform/es/visual-editor.json b/packages/visual-editor/locales/platform/es/visual-editor.json index 100285408a..af1e17df23 100644 --- a/packages/visual-editor/locales/platform/es/visual-editor.json +++ b/packages/visual-editor/locales/platform/es/visual-editor.json @@ -275,6 +275,7 @@ "key": "Clave", "keywordsDisplayName": "Palabras clave Nombre para mostrar", "label": "Etiqueta", + "labelColor": "Color de la etiqueta", "latitude": "Latitud", "limit": "Límite", "link": "Enlace", diff --git a/packages/visual-editor/locales/platform/et/visual-editor.json b/packages/visual-editor/locales/platform/et/visual-editor.json index 2ffc05e39e..3045e230bd 100644 --- a/packages/visual-editor/locales/platform/et/visual-editor.json +++ b/packages/visual-editor/locales/platform/et/visual-editor.json @@ -275,6 +275,7 @@ "key": "Võti", "keywordsDisplayName": "Märksõnad Kuvatav nimi", "label": "Silt", + "labelColor": "Sildi värv", "latitude": "Laius", "limit": "Piiranguid", "link": "Link", diff --git a/packages/visual-editor/locales/platform/fi/visual-editor.json b/packages/visual-editor/locales/platform/fi/visual-editor.json index f46f8bb818..eddcb12251 100644 --- a/packages/visual-editor/locales/platform/fi/visual-editor.json +++ b/packages/visual-editor/locales/platform/fi/visual-editor.json @@ -275,6 +275,7 @@ "key": "Avain", "keywordsDisplayName": "Avainsanat Näyttönimi", "label": "Merkitä", + "labelColor": "Merkinnän väri", "latitude": "Leveysaste", "limit": "Rajoittaa", "link": "Linkki", diff --git a/packages/visual-editor/locales/platform/fr/visual-editor.json b/packages/visual-editor/locales/platform/fr/visual-editor.json index 7995f345dc..01c4f09e51 100644 --- a/packages/visual-editor/locales/platform/fr/visual-editor.json +++ b/packages/visual-editor/locales/platform/fr/visual-editor.json @@ -275,6 +275,7 @@ "key": "Clé", "keywordsDisplayName": "Nom d'affichage des mots clés", "label": "Étiquette", + "labelColor": "Couleur de l'étiquette", "latitude": "Latitude", "limit": "Limite", "link": "Lien", diff --git a/packages/visual-editor/locales/platform/hr/visual-editor.json b/packages/visual-editor/locales/platform/hr/visual-editor.json index af68b3416b..1a4e6c8e59 100644 --- a/packages/visual-editor/locales/platform/hr/visual-editor.json +++ b/packages/visual-editor/locales/platform/hr/visual-editor.json @@ -275,6 +275,7 @@ "key": "Ključ", "keywordsDisplayName": "Naziv za prikaz ključnih riječi", "label": "Označiti", + "labelColor": "Boja oznake", "latitude": "Širina", "limit": "Ograničiti", "link": "Link", diff --git a/packages/visual-editor/locales/platform/hu/visual-editor.json b/packages/visual-editor/locales/platform/hu/visual-editor.json index 930883970d..8afc28ba7e 100644 --- a/packages/visual-editor/locales/platform/hu/visual-editor.json +++ b/packages/visual-editor/locales/platform/hu/visual-editor.json @@ -275,6 +275,7 @@ "key": "Kulcsfontosságú", "keywordsDisplayName": "Kulcsszavak Megjelenített név", "label": "Címke", + "labelColor": "Címke színe", "latitude": "Szélesség", "limit": "Határ", "link": "Link", diff --git a/packages/visual-editor/locales/platform/it/visual-editor.json b/packages/visual-editor/locales/platform/it/visual-editor.json index 4f315b4034..5a9198a0f5 100644 --- a/packages/visual-editor/locales/platform/it/visual-editor.json +++ b/packages/visual-editor/locales/platform/it/visual-editor.json @@ -275,6 +275,7 @@ "key": "Chiave", "keywordsDisplayName": "Nome visualizzato delle parole chiave", "label": "Etichetta", + "labelColor": "Colore dell'etichetta", "latitude": "Latitudine", "limit": "Limite", "link": "Collegamento", diff --git a/packages/visual-editor/locales/platform/ja/visual-editor.json b/packages/visual-editor/locales/platform/ja/visual-editor.json index 8c2f7fb44f..b59265212e 100644 --- a/packages/visual-editor/locales/platform/ja/visual-editor.json +++ b/packages/visual-editor/locales/platform/ja/visual-editor.json @@ -275,6 +275,7 @@ "key": "キー", "keywordsDisplayName": "キーワードの表示名", "label": "ラベル", + "labelColor": "ラベルの色", "latitude": "緯度", "limit": "制限", "link": "リンク", diff --git a/packages/visual-editor/locales/platform/lt/visual-editor.json b/packages/visual-editor/locales/platform/lt/visual-editor.json index ebb5157dce..4d78e14351 100644 --- a/packages/visual-editor/locales/platform/lt/visual-editor.json +++ b/packages/visual-editor/locales/platform/lt/visual-editor.json @@ -275,6 +275,7 @@ "key": "Raktas", "keywordsDisplayName": "Raktiniai žodžiai Rodomas pavadinimas", "label": "Etiketė", + "labelColor": "Etiketės spalva", "latitude": "Platuma", "limit": "Limit", "link": "Nuoroda", diff --git a/packages/visual-editor/locales/platform/lv/visual-editor.json b/packages/visual-editor/locales/platform/lv/visual-editor.json index 4c63efe5ca..eaf93d4eeb 100644 --- a/packages/visual-editor/locales/platform/lv/visual-editor.json +++ b/packages/visual-editor/locales/platform/lv/visual-editor.json @@ -275,6 +275,7 @@ "key": "Atslēga", "keywordsDisplayName": "Atslēgvārdi Parādāmais nosaukums", "label": "Etiķete", + "labelColor": "Etiķetes krāsa", "latitude": "Platums", "limit": "Ierobežot", "link": "Saite", diff --git a/packages/visual-editor/locales/platform/nb/visual-editor.json b/packages/visual-editor/locales/platform/nb/visual-editor.json index 0328f3cd5b..68fc76da15 100644 --- a/packages/visual-editor/locales/platform/nb/visual-editor.json +++ b/packages/visual-editor/locales/platform/nb/visual-editor.json @@ -275,6 +275,7 @@ "key": "Nøkkel", "keywordsDisplayName": "Visningsnavn for søkeord", "label": "Merkelapp", + "labelColor": "Merkelappfarge", "latitude": "Breddegrad", "limit": "Begrense", "link": "Lenke", diff --git a/packages/visual-editor/locales/platform/nl/visual-editor.json b/packages/visual-editor/locales/platform/nl/visual-editor.json index 695957fddc..492fc997ec 100644 --- a/packages/visual-editor/locales/platform/nl/visual-editor.json +++ b/packages/visual-editor/locales/platform/nl/visual-editor.json @@ -275,6 +275,7 @@ "key": "Sleutel", "keywordsDisplayName": "Trefwoorden Weergavenaam", "label": "Label", + "labelColor": "Labelkleur", "latitude": "Breedte", "limit": "Beperken", "link": "Link", diff --git a/packages/visual-editor/locales/platform/pl/visual-editor.json b/packages/visual-editor/locales/platform/pl/visual-editor.json index 74553c7a00..c4e3f5cf85 100644 --- a/packages/visual-editor/locales/platform/pl/visual-editor.json +++ b/packages/visual-editor/locales/platform/pl/visual-editor.json @@ -275,6 +275,7 @@ "key": "Klucz", "keywordsDisplayName": "Nazwa wyświetlana słów kluczowych", "label": "Etykieta", + "labelColor": "Kolor etykiety", "latitude": "Szerokość", "limit": "Limit", "link": "Link", diff --git a/packages/visual-editor/locales/platform/pt/visual-editor.json b/packages/visual-editor/locales/platform/pt/visual-editor.json index a8b3d3f89d..b68b7e4b08 100644 --- a/packages/visual-editor/locales/platform/pt/visual-editor.json +++ b/packages/visual-editor/locales/platform/pt/visual-editor.json @@ -275,6 +275,7 @@ "key": "Chave", "keywordsDisplayName": "Nome de exibição de palavras-chave", "label": "Rótulo", + "labelColor": "Cor do rótulo", "latitude": "Latitude", "limit": "Limite", "link": "Link", diff --git a/packages/visual-editor/locales/platform/ro/visual-editor.json b/packages/visual-editor/locales/platform/ro/visual-editor.json index 4fb9aa99c3..df70115059 100644 --- a/packages/visual-editor/locales/platform/ro/visual-editor.json +++ b/packages/visual-editor/locales/platform/ro/visual-editor.json @@ -275,6 +275,7 @@ "key": "Cheie", "keywordsDisplayName": "Nume afișat pentru cuvinte cheie", "label": "Eticheta", + "labelColor": "Culoarea etichetei", "latitude": "Latitudine", "limit": "Limită", "link": "Legătură", diff --git a/packages/visual-editor/locales/platform/sk/visual-editor.json b/packages/visual-editor/locales/platform/sk/visual-editor.json index 2c072f56fb..0f821b11b4 100644 --- a/packages/visual-editor/locales/platform/sk/visual-editor.json +++ b/packages/visual-editor/locales/platform/sk/visual-editor.json @@ -275,6 +275,7 @@ "key": "kľúč", "keywordsDisplayName": "Zobrazovaný názov kľúčových slov", "label": "Štítok", + "labelColor": "Farba štítka", "latitude": "Šírka", "limit": "Obmedzenie", "link": "Prepojiť", diff --git a/packages/visual-editor/locales/platform/sv/visual-editor.json b/packages/visual-editor/locales/platform/sv/visual-editor.json index fc1df9bfff..158226d3f0 100644 --- a/packages/visual-editor/locales/platform/sv/visual-editor.json +++ b/packages/visual-editor/locales/platform/sv/visual-editor.json @@ -275,6 +275,7 @@ "key": "Nyckel", "keywordsDisplayName": "Sökord Visningsnamn", "label": "Märka", + "labelColor": "Etikettfärg", "latitude": "Latitud", "limit": "Begränsa", "link": "Länk", diff --git a/packages/visual-editor/locales/platform/tr/visual-editor.json b/packages/visual-editor/locales/platform/tr/visual-editor.json index 5f2e090a0a..cc36b003cb 100644 --- a/packages/visual-editor/locales/platform/tr/visual-editor.json +++ b/packages/visual-editor/locales/platform/tr/visual-editor.json @@ -275,6 +275,7 @@ "key": "Anahtar", "keywordsDisplayName": "Anahtar Kelimeler Görünen Ad", "label": "Etiket", + "labelColor": "Etiket Rengi", "latitude": "Enlem", "limit": "Sınırlamak", "link": "Bağlantı", diff --git a/packages/visual-editor/locales/platform/zh-TW/visual-editor.json b/packages/visual-editor/locales/platform/zh-TW/visual-editor.json index 3094bce092..b59ec2ba24 100644 --- a/packages/visual-editor/locales/platform/zh-TW/visual-editor.json +++ b/packages/visual-editor/locales/platform/zh-TW/visual-editor.json @@ -275,6 +275,7 @@ "key": "鍵", "keywordsDisplayName": "關鍵字顯示名稱", "label": "標籤", + "labelColor": "標籤顏色", "latitude": "緯度", "limit": "限制", "link": "鏈接", diff --git a/packages/visual-editor/locales/platform/zh/visual-editor.json b/packages/visual-editor/locales/platform/zh/visual-editor.json index 36a0351747..389e5bd2f3 100644 --- a/packages/visual-editor/locales/platform/zh/visual-editor.json +++ b/packages/visual-editor/locales/platform/zh/visual-editor.json @@ -275,6 +275,7 @@ "key": "钥匙", "keywordsDisplayName": "关键词显示名称", "label": "标签", + "labelColor": "标签颜色", "latitude": "纬度", "limit": "限制", "link": "关联", diff --git a/packages/visual-editor/src/components/atoms/cta.test.ts b/packages/visual-editor/src/components/atoms/cta.test.ts new file mode 100644 index 0000000000..d151042cd8 --- /dev/null +++ b/packages/visual-editor/src/components/atoms/cta.test.ts @@ -0,0 +1,123 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { CTA, CTAProps } from "./cta.tsx"; + +vi.mock("../../hooks/useDocument.tsx", () => ({ + useDocument: () => ({}), +})); + +vi.mock("../../hooks/useBackground.tsx", () => ({ + useBackground: () => ({ + selectedColor: "white", + contrastingColor: "black", + isDarkColor: false, + }), +})); + +vi.mock("@yext/pages-components", () => ({ + getDirections: () => "#", + Link: ({ + children, + cta: _cta, + eventName: _eventName, + ...props + }: { + children: React.ReactNode; + cta: unknown; + eventName?: string; + } & React.AnchorHTMLAttributes) => + React.createElement("a", { href: "#", ...props }, children), +})); + +const renderCta = (props: Partial = {}): void => { + render( + React.createElement(CTA, { + label: "Call to action", + link: "#", + normalizeLink: false, + variant: "primary", + ...props, + }) + ); +}; + +describe("CTA text color", () => { + it("uses a palette color for primary link text and icons", () => { + renderCta({ + textColor: { + selectedColor: "palette-secondary", + contrastingColor: "palette-secondary-contrast", + }, + }); + + const link = screen.getByRole("link"); + expect(link.style.color).toBe("var(--colors-palette-secondary)"); + expect(link.querySelector("svg")?.style.color).toBe(""); + }); + + it("uses a custom color for a primary button", () => { + renderCta({ + actionType: "button", + textColor: { selectedColor: "[#00E5FF]", contrastingColor: "black" }, + }); + + expect(screen.getByRole("button").style.color).toBe("rgb(0, 229, 255)"); + }); + + it("uses the fill contrast color when text color is Default", () => { + renderCta({ + color: { + selectedColor: "palette-primary", + contrastingColor: "palette-primary-contrast", + }, + }); + + expect(screen.getByRole("link").style.color).toBe( + "var(--colors-palette-primary-contrast)" + ); + }); + + it("uses the selected text color for a disabled primary CTA", () => { + renderCta({ + disabled: true, + textColor: { selectedColor: "black", contrastingColor: "white" }, + }); + + expect(screen.getByRole("button").style.color).toBe("black"); + }); + + it("ignores text color for non-primary CTAs", () => { + renderCta({ + variant: "secondary", + color: { + selectedColor: "palette-primary", + contrastingColor: "palette-primary-contrast", + }, + textColor: { + selectedColor: "palette-secondary", + contrastingColor: "palette-secondary-contrast", + }, + }); + + expect(screen.getByRole("link").style.color).toBe( + "var(--colors-palette-primary)" + ); + }); + + it("lets an external-link icon inherit the primary text color", () => { + renderCta({ + openInNewTab: true, + textColor: { selectedColor: "white", contrastingColor: "black" }, + }); + + const link = screen.getByRole("link"); + expect(link.style.color).toBe("white"); + expect(link.querySelectorAll("svg")).toHaveLength(2); + expect( + Array.from(link.querySelectorAll("svg")).every( + (icon) => icon.style.color === "" + ) + ).toBe(true); + }); +}); diff --git a/packages/visual-editor/src/components/atoms/cta.tsx b/packages/visual-editor/src/components/atoms/cta.tsx index 2b26d30263..d38f597643 100644 --- a/packages/visual-editor/src/components/atoms/cta.tsx +++ b/packages/visual-editor/src/components/atoms/cta.tsx @@ -51,7 +51,10 @@ export type CTAProps = { event: React.MouseEvent ) => void; disabled?: boolean; + /** Sets the primary fill, secondary border and text, or link text color. */ color?: ThemeColor; + /** Overrides the text and icon color for primary CTAs. */ + textColor?: ThemeColor; openInNewTab?: boolean; /** * When true and variant is "link", applies vertical padding (py-3) to the CTA. @@ -241,6 +244,7 @@ export const CTA = (props: CTAProps) => { onClick, disabled = false, color, + textColor, openInNewTab = false, setPadding = false, actionType = "link", @@ -296,13 +300,14 @@ export const CTA = (props: CTAProps) => { ))); const dynamicStyle: React.CSSProperties = (() => { const bg = getThemeColorCssValue(color?.selectedColor); - const textColor = getThemeColorCssValue(color?.contrastingColor); const border = bg; if (variant === "primary") { return { backgroundColor: bg, - color: textColor, + color: getThemeColorCssValue( + textColor?.selectedColor ?? color?.contrastingColor + ), borderColor: border, }; } diff --git a/packages/visual-editor/src/components/atoms/hoursStatus.tsx b/packages/visual-editor/src/components/atoms/hoursStatus.tsx index fce7ab5713..486a8a86ea 100644 --- a/packages/visual-editor/src/components/atoms/hoursStatus.tsx +++ b/packages/visual-editor/src/components/atoms/hoursStatus.tsx @@ -19,6 +19,7 @@ export interface HoursStatusAtomProps { timezone?: string; boldCurrentStatus?: boolean; bodyVariant?: "lg" | "base" | "sm"; + style?: React.CSSProperties; } export const HoursStatusAtom = React.memo( @@ -33,6 +34,7 @@ export const HoursStatusAtom = React.memo( timezone, boldCurrentStatus = true, bodyVariant = "lg", + style, }: HoursStatusAtomProps): any => { const { t, i18n } = useTranslation(); @@ -115,7 +117,10 @@ export const HoursStatusAtom = React.memo( } return ( -
+
{(showCurrentStatus || isComingSoon) && hoursCurrentTemplateOverride(params, t, boldCurrentStatus)} {!isComingSoon && diff --git a/packages/visual-editor/src/components/atoms/phone.test.ts b/packages/visual-editor/src/components/atoms/phone.test.ts new file mode 100644 index 0000000000..3818d5af60 --- /dev/null +++ b/packages/visual-editor/src/components/atoms/phone.test.ts @@ -0,0 +1,125 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import { PhoneAtom } from "./phone.tsx"; + +vi.mock("./cta.tsx", () => ({ + CTA: ({ label, color }: { label: React.ReactNode; color?: unknown }) => + React.createElement("a", { + href: "#", + "data-testid": "phone-link", + "data-color": JSON.stringify(color), + children: label, + }), +})); + +describe("PhoneAtom colors", () => { + it("applies independent palette colors to the label, number, and icon", () => { + const { container } = render( + React.createElement(PhoneAtom, { + phoneNumber: "+12025550123", + label: "Phone", + format: "domestic", + includeHyperlink: false, + includeIcon: true, + labelColor: { + selectedColor: "palette-primary", + contrastingColor: "palette-primary-contrast", + }, + linkColor: { + selectedColor: "palette-secondary", + contrastingColor: "palette-secondary-contrast", + }, + iconColor: { + selectedColor: "palette-tertiary", + contrastingColor: "palette-tertiary-contrast", + }, + }) + ); + + expect( + screen.getByText("Phone").classList.contains("text-palette-primary") + ).toBe(true); + expect( + screen + .getByText("(202) 555-0123") + .classList.contains("text-palette-secondary") + ).toBe(true); + expect( + container + .querySelector("svg") + ?.classList.contains("text-palette-tertiary") + ).toBe(true); + }); + + it("applies independent custom colors to the label, number, and icon", () => { + const { container } = render( + React.createElement(PhoneAtom, { + phoneNumber: "+12025550123", + label: "Phone", + format: "domestic", + includeHyperlink: false, + includeIcon: true, + labelColor: { selectedColor: "[#112233]", contrastingColor: "white" }, + linkColor: { selectedColor: "[#445566]", contrastingColor: "white" }, + iconColor: { selectedColor: "[#778899]", contrastingColor: "black" }, + }) + ); + + expect(screen.getByText("Phone").style.color).toBe("rgb(17, 34, 51)"); + expect(screen.getByText("(202) 555-0123").style.color).toBe( + "rgb(68, 85, 102)" + ); + expect(container.querySelector("svg")?.style.color).toBe( + "rgb(119, 136, 153)" + ); + }); + + it("keeps the phone link color separate from label and icon colors", () => { + const { container } = render( + React.createElement(PhoneAtom, { + phoneNumber: "+12025550123", + label: "Phone", + format: "domestic", + includeHyperlink: true, + includeIcon: true, + backgroundColor: { + selectedColor: "palette-quaternary", + contrastingColor: "palette-quaternary-contrast", + }, + labelColor: { + selectedColor: "palette-primary", + contrastingColor: "palette-primary-contrast", + }, + linkColor: { + selectedColor: "palette-secondary", + contrastingColor: "palette-secondary-contrast", + }, + iconColor: { + selectedColor: "palette-tertiary", + contrastingColor: "palette-tertiary-contrast", + }, + }) + ); + + expect( + screen.getByText("Phone").classList.contains("text-palette-primary") + ).toBe(true); + expect(screen.getByTestId("phone-link").getAttribute("data-color")).toBe( + JSON.stringify({ + selectedColor: "palette-secondary", + contrastingColor: "palette-secondary-contrast", + }) + ); + expect( + container + .querySelector("svg") + ?.classList.contains("text-palette-tertiary") + ).toBe(true); + expect( + container + .querySelector("svg") + ?.parentElement?.classList.contains("bg-palette-quaternary") + ).toBe(true); + }); +}); diff --git a/packages/visual-editor/src/components/atoms/phone.tsx b/packages/visual-editor/src/components/atoms/phone.tsx index fbd877ce96..c386c21419 100644 --- a/packages/visual-editor/src/components/atoms/phone.tsx +++ b/packages/visual-editor/src/components/atoms/phone.tsx @@ -6,6 +6,8 @@ import { ThemeColor } from "../../utils/themeConfigOptions.ts"; import { getBackgroundColorClasses, getBackgroundColorStyle, + getTextColorClass, + getTextColorStyle, } from "../../utils/colors.ts"; export type PhoneAtomProps = { @@ -17,6 +19,8 @@ export type PhoneAtomProps = { includeHyperlink: boolean; includeIcon: boolean; linkColor?: ThemeColor; + labelColor?: ThemeColor; + iconColor?: ThemeColor; onClick?: () => void; }; @@ -43,12 +47,22 @@ export const PhoneAtom = (props: PhoneAtomProps) => { )}`} style={getBackgroundColorStyle(props.backgroundColor)} > - +
) : ( - + ))} - {props.label && {props.label}} + {props.label && ( + + {props.label} + + )} {props.includeHyperlink ? ( { alwaysHideCaret={true} /> ) : ( - {formattedPhoneNumber} + {formattedPhoneNumber} )}
); diff --git a/packages/visual-editor/src/components/atoms/timestamp.tsx b/packages/visual-editor/src/components/atoms/timestamp.tsx index 0ee3acd54c..8427d12287 100644 --- a/packages/visual-editor/src/components/atoms/timestamp.tsx +++ b/packages/visual-editor/src/components/atoms/timestamp.tsx @@ -1,3 +1,6 @@ +import { ThemeColor } from "../../utils/themeConfigOptions.ts"; +import { getTextColorClass, getTextColorStyle } from "../../utils/colors.ts"; + const format1: Intl.DateTimeFormatOptions = { month: "short", day: "numeric", @@ -30,6 +33,7 @@ export type TimestampAtomProps = { locale?: string; dateFormatOverride?: Omit; dateTimeFormatOverride?: Omit; + textColor?: ThemeColor; }; type TimestampFormatterPropsType = { @@ -113,6 +117,7 @@ export const TimestampAtom = ({ locale, dateFormatOverride, dateTimeFormatOverride, + textColor, }: TimestampAtomProps): JSX.Element => { let timestamp; try { @@ -144,7 +149,10 @@ export const TimestampAtom = ({ const formattedTimestamp = timestamp.replaceAll("\u202F", " "); return ( -
+
{formattedTimestamp}
); diff --git a/packages/visual-editor/src/components/contentBlocks/Address.tsx b/packages/visual-editor/src/components/contentBlocks/Address.tsx index b34f52292f..8da8ebea98 100644 --- a/packages/visual-editor/src/components/contentBlocks/Address.tsx +++ b/packages/visual-editor/src/components/contentBlocks/Address.tsx @@ -23,6 +23,7 @@ import { } from "../../utils/themeConfigOptions.ts"; import { resolveDataFromParent } from "../../editor/ParentData.tsx"; import { YextComponentConfig, YextFields } from "../../fields/fields.ts"; +import { getTextColorClass, getTextColorStyle } from "../../utils/colors.ts"; /** Props for the Address component */ export interface AddressProps { @@ -50,7 +51,14 @@ export interface AddressProps { /** The variant of the get directions button */ ctaVariant: CTAVariant; + /** Sets the directions CTA fill, border, or link text color. */ color?: ThemeColor; + + /** The color of the address text. */ + textColor?: ThemeColor; + + /** The text and icon color for a primary get directions CTA. */ + ctaTextColor?: ThemeColor; }; /** @internal */ @@ -105,6 +113,16 @@ export const AddressStyleFields: YextFields = { label: msg("fields.linkColor", "Link Color"), options: "SITE_COLOR", }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, + ctaTextColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, }; export const addressFields: YextFields = { @@ -163,7 +181,10 @@ const AddressComponent: PuckComponent = (props) => { ); return showAddress ? ( -
+
= (props) => { target="_blank" variant={styles.ctaVariant} color={resolvedColor} + textColor={styles.ctaTextColor} /> )}
@@ -218,6 +240,11 @@ export const resolveAddressFields = ( "styles.objectFields.color.visible", showGetDirectionsLink && showColor ); + updatedFields = setDeep( + updatedFields, + "styles.objectFields.ctaTextColor.visible", + showGetDirectionsLink && ctaVariant === "primary" + ); return updatedFields; }; diff --git a/packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx b/packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx index 4f7a7f17dd..6577c11e0a 100644 --- a/packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx +++ b/packages/visual-editor/src/components/contentBlocks/CTAGroup.tsx @@ -1,4 +1,4 @@ -import { PuckComponent, setDeep } from "@puckeditor/core"; +import { createUsePuck, PuckComponent, setDeep } from "@puckeditor/core"; import { ThemeColor, ThemeOptions } from "../../utils/themeConfigOptions.ts"; import { CTA } from "../atoms/cta.tsx"; import { PresetImageType } from "../../types/types.ts"; @@ -13,8 +13,10 @@ import { isNonNormalizableLinkType } from "../../utils/normalizeLink.ts"; import { toPuckFields, YextComponentConfig, + YextCustomFieldRenderProps, YextFields, } from "../../fields/fields.ts"; +import { YextAutoField } from "../../fields/YextAutoField.tsx"; // TODO: re-enable CTA Group @@ -27,7 +29,10 @@ type BasicCTAProps = { variant: CTAVariant; /** The image to use if the CTA is set to preset image */ presetImage?: PresetImageType; + /** Sets the primary fill, secondary border and text, or link text color. */ color?: ThemeColor; + /** The text and icon color for the primary variant. */ + textColor?: ThemeColor; }; const defaultButton: BasicCTAProps = { @@ -49,6 +54,47 @@ export interface CTAGroupProps { buttons: BasicCTAProps[]; } +const usePuck = createUsePuck(); + +const CTAGroupTextColorField = ({ + name, + value, + onChange, + readOnly, +}: YextCustomFieldRenderProps): JSX.Element | null => { + const button = usePuck((state) => { + const itemSelector = state.appState.ui.itemSelector; + const buttonIndex = /^buttons\[(\d+)\]\.textColor$/.exec(name)?.[1]; + const buttons = itemSelector + ? state.getItemBySelector(itemSelector)?.props.buttons + : undefined; + + return buttonIndex !== undefined && Array.isArray(buttons) + ? (buttons[Number(buttonIndex)] as BasicCTAProps) + : undefined; + }); + + if ( + button?.variant !== "primary" || + getCTAType(button.entityField).ctaType === "presetImage" + ) { + return null; + } + + return ( + + ); +}; + const ctaGroupFields: YextFields = { buttons: { type: "array", @@ -83,6 +129,13 @@ const ctaGroupFields: YextFields = { label: msg("fields.color", "Color"), options: "SITE_COLOR", }, + textColor: { + type: "custom", + label: msg("fields.textColor", "Text Color"), + render: (props: YextCustomFieldRenderProps) => ( + + ), + }, }, getItemSummary: (_: BasicCTAProps, i?: number) => pt("cta", "CTA") + " " + ((i ?? 0) + 1), @@ -144,6 +197,7 @@ const CTAGroupComponent: PuckComponent = ({ buttons }) => { presetImageType={button.presetImage} className="truncate w-full" color={button.color} + textColor={button.textColor} />
) diff --git a/packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx b/packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx index e1a7d215e3..249694c220 100644 --- a/packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx +++ b/packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx @@ -45,7 +45,10 @@ export interface CTAWrapperProps { variant: CTAVariant; /** The image to use if the CTA is set to preset image */ presetImage?: PresetImageType; + /** Sets the primary fill, secondary border and text, or link text color. */ color?: ThemeColor; + /** The text and icon color for the primary variant. */ + textColor?: ThemeColor; }; /** Additional CSS classes to apply to the CTA. */ @@ -165,6 +168,11 @@ const ctaWrapperFields: YextFields = { label: msg("fields.color", "Color"), options: "SITE_COLOR", }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, }, }, }; @@ -264,6 +272,7 @@ const CTAWrapperComponent: PuckComponent = (props) => { } eventName={eventName} color={styles.color} + textColor={styles.textColor} id={data.customId} ariaLabel={actionType === "button" ? resolvedAriaLabel : undefined} dataAttributes={dataAttributeProps} @@ -350,6 +359,11 @@ export const CTAWrapper: YextComponentConfig = { const showColor = isCtaVariantWithColor(ctaVariant) && effectiveCtaType !== "presetImage"; setDeep(updatedFields, "styles.objectFields.color.visible", showColor); + setDeep( + updatedFields, + "styles.objectFields.textColor.visible", + ctaVariant === "primary" && effectiveCtaType !== "presetImage" + ); if (data.props.parentData) { return updatedFields; diff --git a/packages/visual-editor/src/components/contentBlocks/Emails.tsx b/packages/visual-editor/src/components/contentBlocks/Emails.tsx index 53dc015d69..c7b88b8451 100644 --- a/packages/visual-editor/src/components/contentBlocks/Emails.tsx +++ b/packages/visual-editor/src/components/contentBlocks/Emails.tsx @@ -20,6 +20,7 @@ import { YextComponentConfig, YextFields, } from "../../fields/fields.ts"; +import { getTextColorClass, getTextColorStyle } from "../../utils/colors.ts"; export interface EmailsProps { data: { @@ -31,6 +32,8 @@ export interface EmailsProps { showIcon?: boolean; /** The color applied to both the email icon background and the email link. */ color?: ThemeColor; + /** The color of the email icon. */ + iconColor?: ThemeColor; }; /** @internal Event name to be used for click analytics */ @@ -82,6 +85,11 @@ export const EmailsFields: YextFields = { label: msg("fields.color", "Color"), options: "SITE_COLOR", }, + iconColor: { + type: "basicSelector", + label: msg("fields.iconColor", "Icon Color"), + options: "SITE_COLOR", + }, }, }, }; @@ -126,7 +134,10 @@ const EmailsComponent: PuckComponent = (props) => { background={styles?.color ?? backgroundColors.background2.value} className="h-10 w-10 shrink-0 flex justify-center rounded-full items-center" > - + )} = { @@ -23,9 +26,18 @@ const getDirectionsFields: YextFields = { label: msg("fields.color", "Color"), options: "SITE_COLOR", }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, }; -const GetDirectionsComponent = ({ variant, color }: GetDirectionsProps) => { +const GetDirectionsComponent = ({ + variant, + color, + textColor, +}: GetDirectionsProps) => { const { t } = useTranslation(); return ( @@ -39,6 +51,7 @@ const GetDirectionsComponent = ({ variant, color }: GetDirectionsProps) => { target="_blank" variant={variant} color={color} + textColor={textColor} /> ); }; @@ -54,6 +67,7 @@ export const GetDirections: YextComponentConfig = { const ctaVariant = data.props.variant; const showColor = isCtaVariantWithColor(ctaVariant); setDeep(updatedFields, "color.visible", showColor); + setDeep(updatedFields, "textColor.visible", ctaVariant === "primary"); return updatedFields; }, render: (props) => , diff --git a/packages/visual-editor/src/components/contentBlocks/HoursStatus.tsx b/packages/visual-editor/src/components/contentBlocks/HoursStatus.tsx index 811e9c3479..fe4b750136 100644 --- a/packages/visual-editor/src/components/contentBlocks/HoursStatus.tsx +++ b/packages/visual-editor/src/components/contentBlocks/HoursStatus.tsx @@ -9,6 +9,8 @@ import { msg, pt } from "../../utils/i18n/platform.ts"; import { HoursStatusAtom } from "../atoms/hoursStatus.tsx"; import { resolveDataFromParent } from "../../editor/ParentData.tsx"; import { YextComponentConfig, YextFields } from "../../fields/fields.ts"; +import { ThemeColor } from "../../utils/themeConfigOptions.ts"; +import { getTextColorClass, getTextColorStyle } from "../../utils/colors.ts"; export interface HoursStatusProps { data: { @@ -29,6 +31,8 @@ export interface HoursStatusProps { className?: string; /** The body size variant */ bodyVariant?: "lg" | "base" | "sm"; + /** The color of the hours status text. */ + textColor?: ThemeColor; }; /** @internal */ @@ -90,6 +94,11 @@ export const hoursStatusWrapperFields: YextFields = { { label: msg("fields.options.long", "Long"), value: "long" }, ], }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, }, }, }; @@ -118,12 +127,13 @@ const HoursStatusWrapper: PuckComponent = ({ hours={hours ?? {}} comingSoon={comingSoon} timezone={timezone} - className={styles.className} + className={`${styles.className ?? ""} ${getTextColorClass(styles.textColor) ?? ""}`} showCurrentStatus={styles.showCurrentStatus} showDayNames={styles.showDayNames} timeFormat={styles.timeFormat} dayOfWeekFormat={styles.dayOfWeekFormat} bodyVariant={styles.bodyVariant} + style={getTextColorStyle(styles.textColor)} /> ) : puck.isEditing ? ( diff --git a/packages/visual-editor/src/components/contentBlocks/HoursTable.tsx b/packages/visual-editor/src/components/contentBlocks/HoursTable.tsx index 17326a3482..d41ef79335 100644 --- a/packages/visual-editor/src/components/contentBlocks/HoursTable.tsx +++ b/packages/visual-editor/src/components/contentBlocks/HoursTable.tsx @@ -10,6 +10,8 @@ import { YextEntityField } from "../../editor/YextEntityFieldSelector.tsx"; import { msg, pt } from "../../utils/i18n/platform.ts"; import { Body } from "../atoms/body.tsx"; import { YextComponentConfig, YextFields } from "../../fields/fields.ts"; +import { ThemeColor } from "../../utils/themeConfigOptions.ts"; +import { getTextColorClass, getTextColorStyle } from "../../utils/colors.ts"; /** Props for the HoursTable component. */ export interface HoursTableProps { @@ -29,6 +31,8 @@ export interface HoursTableProps { showAdditionalHoursText: boolean; /** Alignment of the text in the hours table */ alignment: "items-start" | "items-center"; + /** The color of the hours text. */ + textColor?: ThemeColor; }; } @@ -96,6 +100,11 @@ export const hoursTableFields: YextFields = { }, ], }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, }, }, }; @@ -112,7 +121,10 @@ const VisualEditorHoursTable: PuckComponent = (props) => { }; return hours || comingSoon ? ( -
+
= { label: msg("fields.color", "Color"), options: "SITE_COLOR", }, + labelColor: { + type: "basicSelector", + label: msg("fields.labelColor", "Label Color"), + options: "SITE_COLOR", + }, + iconColor: { + type: "basicSelector", + label: msg("fields.iconColor", "Icon Color"), + options: "SITE_COLOR", + }, }; export const defaultPhoneDataProps: PhoneProps["data"] = { @@ -134,6 +148,8 @@ const PhoneComponent = ({ data, styles, parentData }: PhoneProps) => { includeHyperlink={styles.includePhoneHyperlink} includeIcon={styles.includeIcon ?? true} linkColor={styles.color} + labelColor={styles.labelColor} + iconColor={styles.iconColor} /> ); diff --git a/packages/visual-editor/src/components/contentBlocks/PhoneList.tsx b/packages/visual-editor/src/components/contentBlocks/PhoneList.tsx index 0b39854e30..df6e231843 100644 --- a/packages/visual-editor/src/components/contentBlocks/PhoneList.tsx +++ b/packages/visual-editor/src/components/contentBlocks/PhoneList.tsx @@ -137,6 +137,8 @@ export const PhoneListComponent: PuckComponent = (props) => { includeHyperlink={styles.includePhoneHyperlink} includeIcon={styles.includeIcon ?? true} linkColor={styles.color} + labelColor={styles.labelColor} + iconColor={styles.iconColor} />
diff --git a/packages/visual-editor/src/components/contentBlocks/TextList.tsx b/packages/visual-editor/src/components/contentBlocks/TextList.tsx index e4767ab00e..21ebeb0c05 100644 --- a/packages/visual-editor/src/components/contentBlocks/TextList.tsx +++ b/packages/visual-editor/src/components/contentBlocks/TextList.tsx @@ -7,10 +7,16 @@ import { TranslatableString } from "../../types/types.ts"; import { resolveComponentData } from "../../utils/resolveComponentData.tsx"; import { msg, pt } from "../../utils/i18n/platform.ts"; import { YextComponentConfig, YextFields } from "../../fields/fields.ts"; +import { ThemeColor } from "../../utils/themeConfigOptions.ts"; +import { getTextColorClass, getTextColorStyle } from "../../utils/colors.ts"; export interface TextListProps { list: YextEntityField; commaSeparated: boolean; + styles?: { + /** The color of the list text. */ + textColor?: ThemeColor; + }; } export const textListFields: YextFields = { @@ -30,11 +36,23 @@ export const textListFields: YextFields = { { label: msg("fields.options.no", "No"), value: false }, ], }, + styles: { + type: "object", + label: msg("fields.styles", "Styles"), + objectFields: { + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, + }, + }, }; const TextListComponent: PuckComponent = ({ list: textListField, commaSeparated, + styles, puck, }) => { const { i18n } = useTranslation(); @@ -58,11 +76,12 @@ const TextListComponent: PuckComponent = ({ > {resolvedTextList && resolvedTextList.length > 0 ? (
    {resolvedTextList.map((text, index) => (
  • = { constantValue: [], }, commaSeparated: false, + styles: {}, }, render: (props) => , }; diff --git a/packages/visual-editor/src/components/contentBlocks/Timestamp.tsx b/packages/visual-editor/src/components/contentBlocks/Timestamp.tsx index 23edab893a..f66d4fbfca 100644 --- a/packages/visual-editor/src/components/contentBlocks/Timestamp.tsx +++ b/packages/visual-editor/src/components/contentBlocks/Timestamp.tsx @@ -11,6 +11,7 @@ import { YextComponentConfig, YextFields, } from "../../fields/fields.ts"; +import { ThemeColor } from "../../utils/themeConfigOptions.ts"; export type TimestampProps = { /** The start and end date to display */ @@ -26,6 +27,8 @@ export type TimestampProps = { includeTime: boolean; /** Whether to display an end date */ includeRange: boolean; + /** The color of the timestamp text. */ + textColor?: ThemeColor; }; /** @@ -78,6 +81,11 @@ const timestampFields: YextFields = { { label: msg("fields.options.yes", "Yes"), value: true }, ], }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, }, }, }; @@ -111,6 +119,7 @@ const TimestampComponent: PuckComponent = (props) => { option={option} locale={i18n.language} hideTimeZone + textColor={styles.textColor} /> ); }; diff --git a/packages/visual-editor/src/components/footer/CopyrightMessageSlot.tsx b/packages/visual-editor/src/components/footer/CopyrightMessageSlot.tsx index 02619acfc6..81a2320a0c 100644 --- a/packages/visual-editor/src/components/footer/CopyrightMessageSlot.tsx +++ b/packages/visual-editor/src/components/footer/CopyrightMessageSlot.tsx @@ -8,11 +8,16 @@ import { TranslatableString } from "../../types/types.ts"; import { Body } from "../atoms/body.tsx"; import { useTranslation } from "react-i18next"; import { YextComponentConfig } from "../../fields/fields.ts"; +import { ThemeColor } from "../../utils/themeConfigOptions.ts"; export interface CopyrightMessageSlotProps { data: { text: TranslatableString; }; + styles?: { + /** The color of the copyright text. */ + textColor?: ThemeColor; + }; /** @internal */ desktopContentAlignment?: "left" | "center" | "right"; /** @internal */ @@ -46,6 +51,7 @@ const CopyrightMessageSlotInternal: PuckComponent = ( puck, desktopContentAlignment = "left", mobileContentAlignment = "left", + styles, } = props; const streamDocument = useDocument(); const { i18n } = useTranslation(); @@ -59,6 +65,7 @@ const CopyrightMessageSlotInternal: PuckComponent = ( return resolvedText ? ( , diff --git a/packages/visual-editor/src/components/locator/LocatorResultCard.tsx b/packages/visual-editor/src/components/locator/LocatorResultCard.tsx index fd68ae88a9..39d748c622 100644 --- a/packages/visual-editor/src/components/locator/LocatorResultCard.tsx +++ b/packages/visual-editor/src/components/locator/LocatorResultCard.tsx @@ -149,6 +149,9 @@ export interface LocatorResultCardProps { /** The accent color used for icon backgrounds and contact/action links. */ accentColor?: ThemeColor; + /** The color of result card body text. */ + textColor?: ThemeColor; + /** Settings for the hours block */ hours: { /** The field from the data to use for the hours */ @@ -227,6 +230,8 @@ export interface LocatorResultCardProps { normalizeLink: boolean; /** The variant for the primary CTA */ variant: CTAVariant; + /** The text and icon color for the primary variant of the primary CTA. */ + textColor?: ThemeColor; /** Whether the primary CTA is visible in live mode */ liveVisibility: boolean; /** Static URL to use for primary CTA when an entity page URL is not found */ @@ -243,6 +248,8 @@ export interface LocatorResultCardProps { normalizeLink: boolean; /** The variant for the secondary CTA */ variant: CTAVariant; + /** The text and icon color for the primary variant of the secondary CTA. */ + textColor?: ThemeColor; /** Whether the secondary CTA is visible in live mode */ liveVisibility: boolean; }; @@ -565,6 +572,11 @@ export const LocatorResultCardFields: YextObjectField = label: msg("fields.accentColor", "Accent Color"), options: "SITE_COLOR", }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, hours: { label: msg("fields.hours", "Hours"), type: "object", @@ -712,6 +724,11 @@ export const LocatorResultCardFields: YextObjectField = type: "radio", options: ThemeOptions.CTA_VARIANT, }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, liveVisibility: { label: msg("fields.visibleOnLivePage", "Visible on Live Page"), type: "radio", @@ -768,6 +785,11 @@ export const LocatorResultCardFields: YextObjectField = type: "radio", options: ThemeOptions.CTA_VARIANT, }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, liveVisibility: { label: msg("fields.visibleOnLivePage", "Visible on Live Page"), type: "radio", @@ -960,8 +982,11 @@ export const LocatorResultCard = React.memo( return (
    @@ -1109,6 +1134,7 @@ export const LocatorResultCard = React.memo( ) || t("callToAction", "Call to Action") } variant={props.secondaryCTA.variant} + textColor={props.secondaryCTA.textColor} normalizeLink={props.secondaryCTA.normalizeLink} onClick={handleSecondaryCTAClick} className="basis-full sm:w-auto justify-center" @@ -1177,6 +1203,7 @@ const PrimaryCTA = (props: { defaultValue: "Visit Page for {{name}}", })} variant={primaryCTA.variant} + textColor={primaryCTA.textColor} normalizeLink={primaryCTA.normalizeLink} onClick={handlePrimaryCtaClick} className="basis-full sm:w-auto justify-center" diff --git a/packages/visual-editor/src/components/locator/Results.tsx b/packages/visual-editor/src/components/locator/Results.tsx index 9eba823fc8..25e8c44a55 100644 --- a/packages/visual-editor/src/components/locator/Results.tsx +++ b/packages/visual-editor/src/components/locator/Results.tsx @@ -141,6 +141,16 @@ export const ResultCardPropsField = ({ "objectFields.image.objectFields.constantValue.visible", imageConstantValueEnabled ); + fields = setDeep( + fields, + "objectFields.primaryCTA.objectFields.textColor.visible", + value.primaryCTA?.variant === "primary" + ); + fields = setDeep( + fields, + "objectFields.secondaryCTA.objectFields.textColor.visible", + value.secondaryCTA?.variant === "primary" + ); return fields; }, [entityTypeSourceMap, entityTypeScopes, templateMetadata, value]); diff --git a/packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationCard.tsx b/packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationCard.tsx index c5c9623a3c..ab0cbfa12f 100644 --- a/packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationCard.tsx +++ b/packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationCard.tsx @@ -14,6 +14,7 @@ import { resolveUrlTemplate, } from "../../../utils/urls/resolveUrlTemplate.ts"; import { NearbyLocationDoc } from "./useNearbyLocations.ts"; +import { getTextColorClass, getTextColorStyle } from "../../../utils/colors.ts"; /** A single card for the Nearby Locations Section */ type NearbyLocationCardProps = { @@ -58,7 +59,8 @@ export const NearbyLocationCard: React.FC = ( return ( = { label: msg("fields.accentColor", "Accent Color"), options: "SITE_COLOR", }, + textColor: { + type: "basicSelector", + label: msg("fields.textColor", "Text Color"), + options: "SITE_COLOR", + }, showSectionHeading: { label: msg("fields.showSectionHeading", "Show Section Heading"), type: "radio", @@ -212,7 +224,8 @@ const ReviewsSectionInternal: PuckComponent = (props) => { return (
    diff --git a/packages/visual-editor/src/docs/components.md b/packages/visual-editor/src/docs/components.md index 83c1874956..48a075c9a2 100644 --- a/packages/visual-editor/src/docs/components.md +++ b/packages/visual-editor/src/docs/components.md @@ -11,10 +11,10 @@ Props for the Address component #### Other Props -| Prop | Type | Description | Default | -| :------- | :----------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | -| `data` | `{ address: YextEntityField; }` | | | -| `styles` | `{ showRegion?: boolean; showCountry?: boolean; showGetDirectionsLink: boolean; ctaVariant: CTAVariant; color?: ThemeColor; }` | | | +| Prop | Type | Description | Default | +| :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | +| `data` | `{ address: YextEntityField; }` | | | +| `styles` | `{ showRegion?: boolean; showCountry?: boolean; showGetDirectionsLink: boolean; ctaVariant: CTAVariant; color?: ThemeColor; textColor?: ThemeColor; ctaTextColor?: ThemeColor; }` | | | --- @@ -99,9 +99,10 @@ If 'true', the component is visible on the live page; if 'false', it's hidden. #### Other Props -| Prop | Type | Description | Default | -| :----- | :------------------------------ | :---------- | :------ | -| `data` | `{ text: TranslatableString; }` | | | +| Prop | Type | Description | Default | +| :------- | :------------------------------ | :---------- | :------ | +| `data` | `{ text: TranslatableString; }` | | | +| `styles` | `{ textColor?: ThemeColor; }` | | | --- @@ -156,7 +157,7 @@ Additional CSS classes to apply to the CTA. | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------ | :------ | | `className` | `string` | Additional CSS classes to apply to the CTA. | | | `data` | `{ show?: boolean; actionType?: "link" \| "button"; normalizeLink: boolean; entityField: YextCTAField; buttonText?: TranslatableString; customId?: string; customClass?: string; dataAttributes?: Array<{ key: string; value: string; }>; ariaLabel?: TranslatableString; }` | | | -| `styles` | `{ variant: CTAVariant; presetImage?: PresetImageType; color?: ThemeColor; }` | | | +| `styles` | `{ variant: CTAVariant; presetImage?: PresetImageType; color?: ThemeColor; textColor?: ThemeColor; }` | | | --- @@ -207,10 +208,10 @@ This object contains properties for customizing the component's appearance. #### Other Props -| Prop | Type | Description | Default | -| :------- | :----------------------------------------------------------------- | :---------- | :------ | -| `data` | `{ list: YextEntityField; }` | | | -| `styles` | `{ listLength?: number; showIcon?: boolean; color?: ThemeColor; }` | | | +| Prop | Type | Description | Default | +| :------- | :----------------------------------------------------------------------------------------- | :---------- | :------ | +| `data` | `{ list: YextEntityField; }` | | | +| `styles` | `{ listLength?: number; showIcon?: boolean; color?: ThemeColor; iconColor?: ThemeColor; }` | | | --- @@ -477,10 +478,10 @@ If 'true', the component is visible on the live page; if 'false', it's hidden. #### Other Props -| Prop | Type | Description | Default | -| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | -| `data` | `{ hours: YextEntityField; }` | | | -| `styles` | `{ showCurrentStatus?: boolean; timeFormat?: "12h" \| "24h"; dayOfWeekFormat?: "short" \| "long"; showDayNames?: boolean; className?: string; bodyVariant?: "lg" \| "base" \| "sm"; }` | | | +| Prop | Type | Description | Default | +| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | +| `data` | `{ hours: YextEntityField; }` | | | +| `styles` | `{ showCurrentStatus?: boolean; timeFormat?: "12h" \| "24h"; dayOfWeekFormat?: "short" \| "long"; showDayNames?: boolean; className?: string; bodyVariant?: "lg" \| "base" \| "sm"; textColor?: ThemeColor; }` | | | --- @@ -492,10 +493,10 @@ Props for the HoursTable component. #### Other Props -| Prop | Type | Description | Default | -| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | -| `data` | `{ hours: YextEntityField; }` | | | -| `styles` | `{ startOfWeek: keyof DayOfWeekNames \| "today"; collapseDays: boolean; showAdditionalHoursText: boolean; alignment: "items-start" \| "items-center"; }` | | | +| Prop | Type | Description | Default | +| :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | +| `data` | `{ hours: YextEntityField; }` | | | +| `styles` | `{ startOfWeek: keyof DayOfWeekNames \| "today"; collapseDays: boolean; showAdditionalHoursText: boolean; alignment: "items-start" \| "items-center"; textColor?: ThemeColor; }` | | | --- @@ -600,10 +601,10 @@ The props for the Phone component #### Other Props -| Prop | Type | Description | Default | -| :------- | :--------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | -| `data` | `{ number: YextEntityField; label: TranslatableString; }` | | | -| `styles` | `{ phoneFormat: "domestic" \| "international"; includePhoneHyperlink: boolean; includeIcon?: boolean; color?: ThemeColor; }` | | | +| Prop | Type | Description | Default | +| :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------ | +| `data` | `{ number: YextEntityField; label: TranslatableString; }` | | | +| `styles` | `{ phoneFormat: "domestic" \| "international"; includePhoneHyperlink: boolean; includeIcon?: boolean; color?: ThemeColor; labelColor?: ThemeColor; iconColor?: ThemeColor; }` | | | --- @@ -772,9 +773,9 @@ The Reviews Section displays customer reviews fetched dynamically from the Yext This object contains properties for customizing the component's appearance. -| Prop | Type | Description | Default | -| :------- | :----------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | :------ | -| `styles` | `{ backgroundColor?: ThemeColor; accentColor?: ThemeColor; showSectionHeading: boolean; }` | This object contains properties for customizing the component's appearance. | | +| Prop | Type | Description | Default | +| :------- | :----------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | :------ | +| `styles` | `{ backgroundColor?: ThemeColor; accentColor?: ThemeColor; textColor?: ThemeColor; showSectionHeading: boolean; }` | This object contains properties for customizing the component's appearance. | | #### Other Props @@ -893,6 +894,7 @@ If 'true', the component is visible on the live page; if 'false', it's hidden. | :--------------- | :-------------------------------------- | :---------- | :------ | | `commaSeparated` | `boolean` | | | | `list` | `YextEntityField` | | | +| `styles` | `{ textColor?: ThemeColor; }` | | | ---