Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import { PuckComponent, setDeep } from "@puckeditor/core";
import { msg, pt } from "../../utils/i18n/platform.ts";
import { useDocument } from "../../hooks/useDocument.tsx";
import { resolveComponentData } from "../../utils/resolveComponentData.tsx";
import { resolveLocalizedFooterLinkSection } from "./resolveLocalizedFooterLinkSection.ts";
import { TranslatableString, TranslatableCTA } from "../../types/types.ts";
import { YextEntityField } from "../../editor/YextEntityFieldSelector.tsx";
import { CTA } from "../atoms/cta.tsx";
Expand Down Expand Up @@ -41,52 +41,48 @@ const FooterExpandedLinkSectionSlotInternal: PuckComponent<
const background = useBackground();
const isDarkBackground = background?.isDarkColor ?? false;

const label = resolveComponentData(data.label, i18n.language, streamDocument);
const links = data.links;
const { label, links } = resolveLocalizedFooterLinkSection(
data,
i18n.language,
streamDocument
);

const defaultColor: ThemeColor = isDarkBackground
? { selectedColor: "white", contrastingColor: "black" }
: { selectedColor: "palette-primary-dark", contrastingColor: "white" };
const resolvedColor = styles?.color ?? defaultColor;

if (links.length === 0 && !puck.isEditing) {
return <></>;
}

return (
<div className="flex flex-col gap-6">
<Body className="break-words" color={resolvedColor}>
{label}
</Body>
{label.trim() !== "" && (
<Body className="break-words" color={resolvedColor}>
{label}
</Body>
)}
<div className="flex flex-col gap-4">
{links && links.length > 0
? links.map((linkData, index) => {
const linkLabel = resolveComponentData(
linkData.label,
i18n.language,
streamDocument
);
const link = resolveComponentData(
linkData.link,
i18n.language,
streamDocument
);

return (
<CTA
openInNewTab={linkData.openInNewTab}
key={index}
variant="headerFooterMainLink"
eventName={`cta.expandedFooter.${index}-Link-${index + 1}`}
label={linkLabel}
linkType={linkData.linkType}
link={link}
normalizeLink={
isNonNormalizableLinkType(linkData.linkType)
? false
: (linkData.normalizeLink ?? true)
}
className="justify-center md:justify-start block break-words whitespace-normal"
color={resolvedColor}
/>
);
})
{links.length > 0
? links.map((linkData, index) => (
<CTA
openInNewTab={linkData.openInNewTab}
key={index}
variant="headerFooterMainLink"
eventName={`cta.expandedFooter.${index}-Link-${index + 1}`}
label={linkData.label}
linkType={linkData.linkType}
link={linkData.link}
normalizeLink={
isNonNormalizableLinkType(linkData.linkType)
? false
: (linkData.normalizeLink ?? true)
}
className="justify-center md:justify-start block break-words whitespace-normal"
color={resolvedColor}
/>
))
: puck.isEditing && <div className="h-6 min-w-[100px]" />}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { msg, pt } from "../../utils/i18n/platform.ts";
import { TranslatableString, TranslatableCTA } from "../../types/types.ts";
import { i18nComponentsInstance } from "../../utils/i18n/components.ts";
import { useDocument } from "../../hooks/useDocument.tsx";
import {
getDisplayValue,
resolveComponentData,
} from "../../utils/resolveComponentData.tsx";
import { getDisplayValue } from "../../utils/resolveComponentData.tsx";
import { resolveLocalizedFooterLinkSection } from "./resolveLocalizedFooterLinkSection.ts";
import { CTA } from "../atoms/cta.tsx";
import { useBackground } from "../../hooks/useBackground.tsx";
import { Body } from "../atoms/body.tsx";
Expand Down Expand Up @@ -246,13 +244,19 @@ const FooterExpandedLinksWrapperInternal: PuckComponent<
styles,
desktopContentAlignment = "left",
mobileContentAlignment = "left",
puck,
} = props;
const streamDocument = useDocument();
const { i18n } = useTranslation();
const background = useBackground();
const isDarkBackground = background?.isDarkColor ?? false;

const sections = data.sections || [];
const resolvedSections = (data.sections || []).map((section) =>
resolveLocalizedFooterLinkSection(section, i18n.language, streamDocument)
);
const sections = puck.isEditing
? resolvedSections
: resolvedSections.filter((section) => section.links.length > 0);
const defaultLabelColor = isDarkBackground
? ({ selectedColor: "white", contrastingColor: "black" } as ThemeColor)
: ({ selectedColor: "black", contrastingColor: "white" } as ThemeColor);
Expand Down Expand Up @@ -286,13 +290,6 @@ const FooterExpandedLinksWrapperInternal: PuckComponent<
}
>
{sections.map((section, sectionIndex) => {
const label = resolveComponentData(
section.label,
i18n.language,
streamDocument
);
const links = section.links || [];

return (
<div
key={sectionIndex}
Expand All @@ -301,49 +298,40 @@ const FooterExpandedLinksWrapperInternal: PuckComponent<
mobileContentAlignment,
})}
>
<Body
className="break-words font-link-fontWeight font-body-fontFamily font-body-fontWeight"
color={resolvedLabelColor}
>
{label}
</Body>
{section.label.trim() !== "" && (
<Body
className="break-words font-link-fontWeight font-body-fontFamily font-body-fontWeight"
color={resolvedLabelColor}
>
{section.label}
</Body>
)}
<div className="flex flex-col gap-4">
{links.map((linkData, linkIndex) => {
const linkLabel = resolveComponentData(
linkData.label,
i18n.language,
streamDocument
);
const link = resolveComponentData(
linkData.link,
i18n.language,
streamDocument
);

return (
<CTA
openInNewTab={linkData.openInNewTab}
key={linkIndex}
variant="headerFooterMainLink"
eventName={`cta.expandedFooter.${sectionIndex}-Link-${linkIndex + 1}`}
label={linkLabel}
linkType={linkData.linkType}
link={link}
normalizeLink={
isNonNormalizableLinkType(linkData.linkType)
? false
: (linkData.normalizeLink ?? true)
}
className={themeManagerCn(
expandedLinkJustification({
desktopContentAlignment,
mobileContentAlignment,
})
)}
color={resolvedLinkColor}
/>
);
})}
{section.links.length > 0
? section.links.map((linkData, linkIndex) => (
<CTA
openInNewTab={linkData.openInNewTab}
key={linkIndex}
variant="headerFooterMainLink"
eventName={`cta.expandedFooter.${sectionIndex}-Link-${linkIndex + 1}`}
label={linkData.label}
linkType={linkData.linkType}
link={linkData.link}
normalizeLink={
isNonNormalizableLinkType(linkData.linkType)
? false
: (linkData.normalizeLink ?? true)
}
className={themeManagerCn(
expandedLinkJustification({
desktopContentAlignment,
mobileContentAlignment,
})
)}
color={resolvedLinkColor}
/>
))
: puck.isEditing && <div className="h-6 min-w-[100px]" />}
</div>
</div>
);
Expand Down
27 changes: 10 additions & 17 deletions packages/visual-editor/src/components/footer/FooterLinksSlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PuckComponent, setDeep } from "@puckeditor/core";
import { cva } from "class-variance-authority";
import { msg, pt } from "../../utils/i18n/platform.ts";
import { useDocument } from "../../hooks/useDocument.tsx";
import { resolveComponentData } from "../../utils/resolveComponentData.tsx";
import { resolveLocalizedCtas } from "../../utils/resolveLocalizedCtas.ts";
import { CTA } from "../atoms/cta.tsx";
import { TranslatableCTA } from "../../types/types.ts";
import { i18nComponentsInstance } from "../../utils/i18n/components.ts";
Expand Down Expand Up @@ -167,29 +167,22 @@ const FooterLinksSlotInternal: PuckComponent<FooterLinksSlotProps> = (
} = props;
const streamDocument = useDocument();
const { i18n } = useTranslation();
const resolvedLinks = resolveLocalizedCtas(
data.links,
i18n.language,
streamDocument
);

if (!data.links || data.links.length === 0) {
if (resolvedLinks.length === 0) {
return puck.isEditing ? <div className="h-10 min-w-[100px]" /> : <></>;
}

const links = data.links.map((linkData, index) => {
const label = resolveComponentData(
linkData.label,
i18n.language,
streamDocument
);

const link = resolveComponentData(
linkData.link,
i18n.language,
streamDocument
);

const links = resolvedLinks.map((linkData, index) => {
return (
<CTA
key={index}
link={link}
label={label}
link={linkData.link}
label={linkData.label}
linkType={linkData.linkType}
variant={
variant === "primary"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, expect, it } from "vitest";
import { resolveLocalizedFooterLinkSection } from "./resolveLocalizedFooterLinkSection.ts";

describe("resolveLocalizedFooterLinkSection", () => {
it("resolves the section label and removes links that are blank in the active locale", () => {
const section = {
label: { defaultValue: "Resources", fr: "Ressources" },
links: [
{
linkType: "URL" as const,
label: { defaultValue: "About", fr: "À propos" },
link: { defaultValue: "/about", fr: "/fr/about" },
},
{
linkType: "URL" as const,
label: { defaultValue: "Careers", fr: "" },
link: { defaultValue: "/careers", fr: "" },
},
],
};

expect(resolveLocalizedFooterLinkSection(section, "fr")).toEqual({
label: "Ressources",
links: [
{
linkType: "URL",
label: "À propos",
link: "/fr/about",
},
],
});
});

it("keeps valid links when the localized section label is blank", () => {
const section = {
label: { defaultValue: "Resources", fr: "" },
links: [
{
linkType: "URL" as const,
label: { defaultValue: "About", fr: "À propos" },
link: { defaultValue: "/about", fr: "/fr/about" },
},
],
};

expect(resolveLocalizedFooterLinkSection(section, "fr")).toMatchObject({
label: "",
links: [{ label: "À propos", link: "/fr/about" }],
});
});

it("resolves an entity-backed section label", () => {
const section = {
label: {
field: "",
constantValue: { defaultValue: "Resources", fr: "Ressources" },
constantValueEnabled: true,
},
links: [],
};

expect(resolveLocalizedFooterLinkSection(section, "fr")).toEqual({
label: "Ressources",
links: [],
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { type YextEntityField } from "../../editor/YextEntityFieldSelector.tsx";
import {
type TranslatableCTA,
type TranslatableString,
} from "../../types/types.ts";
import { resolveComponentData } from "../../utils/resolveComponentData.tsx";
import {
type ResolvedCTA,
resolveLocalizedCtas,
} from "../../utils/resolveLocalizedCtas.ts";

type FooterLinkSection = {
label: TranslatableString | YextEntityField<TranslatableString>;
links: TranslatableCTA[];
};

export type ResolvedFooterLinkSection<
T extends FooterLinkSection = FooterLinkSection,
> = Omit<T, "label" | "links"> & {
label: string;
links: ResolvedCTA[];
};

export const resolveLocalizedFooterLinkSection = <T extends FooterLinkSection>(
section: T,
locale: string,
streamDocument?: Record<string, any>
): ResolvedFooterLinkSection<T> => {
return {
...section,
label: resolveComponentData(section.label, locale, streamDocument),
links: resolveLocalizedCtas(section.links, locale, streamDocument),
};
};
Loading
Loading