Backend / staging cleanup, performance substrate, and create-flow polish #60
@@ -12,6 +12,21 @@ function columnUsesLargeBreakpointCopy(column: TripleTextBlockColumn): boolean {
|
|||||||
return column.lgTitle !== undefined || column.lgDescription !== undefined;
|
return column.lgTitle !== undefined || column.lgDescription !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function splitDescriptionParagraphs(description: string): {
|
||||||
|
primary: string;
|
||||||
|
secondary?: string;
|
||||||
|
} {
|
||||||
|
const parts = description.split(/\n\n+/).map((part) => part.trim()).filter(Boolean);
|
||||||
|
if (parts.length <= 1) {
|
||||||
|
return { primary: description };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
primary: parts[0] ?? description,
|
||||||
|
secondary: parts.slice(1).join("\n\n"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function TripleTextUseCasesColumn({ column }: { column: TripleTextBlockColumn }) {
|
function TripleTextUseCasesColumn({ column }: { column: TripleTextBlockColumn }) {
|
||||||
return (
|
return (
|
||||||
<article className="flex w-full flex-col gap-[var(--spacing-scale-006)] md:gap-[var(--spacing-scale-008)] lg:gap-[var(--spacing-scale-004)] xl:gap-[var(--spacing-scale-008)]">
|
<article className="flex w-full flex-col gap-[var(--spacing-scale-006)] md:gap-[var(--spacing-scale-008)] lg:gap-[var(--spacing-scale-004)] xl:gap-[var(--spacing-scale-008)]">
|
||||||
@@ -30,6 +45,22 @@ function TripleTextUseCasesColumn({ column }: { column: TripleTextBlockColumn })
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TripleTextStackedColumn({ column }: { column: TripleTextBlockColumn }) {
|
||||||
|
const { primary, secondary } = column.descriptionSecondary
|
||||||
|
? { primary: column.description, secondary: column.descriptionSecondary }
|
||||||
|
: splitDescriptionParagraphs(column.description);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TripleTextUseCasesColumn
|
||||||
|
column={{
|
||||||
|
...column,
|
||||||
|
description: primary,
|
||||||
|
descriptionSecondary: secondary,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function TripleTextBlockColumnLockup({
|
function TripleTextBlockColumnLockup({
|
||||||
column,
|
column,
|
||||||
layoutPreset,
|
layoutPreset,
|
||||||
@@ -38,7 +69,7 @@ function TripleTextBlockColumnLockup({
|
|||||||
layoutPreset: "default" | "useCases";
|
layoutPreset: "default" | "useCases";
|
||||||
}) {
|
}) {
|
||||||
if (layoutPreset === "useCases") {
|
if (layoutPreset === "useCases") {
|
||||||
return <TripleTextUseCasesColumn column={column} />;
|
return <TripleTextStackedColumn column={column} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dual = columnUsesLargeBreakpointCopy(column);
|
const dual = columnUsesLargeBreakpointCopy(column);
|
||||||
@@ -47,24 +78,26 @@ function TripleTextBlockColumnLockup({
|
|||||||
|
|
||||||
if (!dual) {
|
if (!dual) {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<div className="lg:hidden">
|
||||||
|
<TripleTextStackedColumn column={column} />
|
||||||
|
</div>
|
||||||
|
<div className="hidden lg:block">
|
||||||
<ContentLockup
|
<ContentLockup
|
||||||
variant="about"
|
variant="about"
|
||||||
alignment="left"
|
alignment="left"
|
||||||
subtitle={column.title}
|
subtitle={column.title}
|
||||||
description={column.description}
|
description={column.description}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="lg:hidden">
|
<div className="lg:hidden">
|
||||||
<ContentLockup
|
<TripleTextStackedColumn column={column} />
|
||||||
variant="about"
|
|
||||||
alignment="left"
|
|
||||||
subtitle={column.title}
|
|
||||||
description={column.description}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden lg:block">
|
<div className="hidden lg:block">
|
||||||
<ContentLockup
|
<ContentLockup
|
||||||
@@ -105,7 +138,7 @@ function TripleTextBlockView({
|
|||||||
className={`bg-black py-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-064)] ${
|
className={`bg-black py-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-064)] ${
|
||||||
isUseCases
|
isUseCases
|
||||||
? "px-[var(--spacing-scale-032)] md:px-[var(--spacing-scale-096)] lg:px-[calc(var(--spacing-scale-096)+var(--spacing-scale-096))] xl:px-[var(--spacing-scale-160)]"
|
? "px-[var(--spacing-scale-032)] md:px-[var(--spacing-scale-096)] lg:px-[calc(var(--spacing-scale-096)+var(--spacing-scale-096))] xl:px-[var(--spacing-scale-160)]"
|
||||||
: "px-[calc(var(--spacing-scale-032)+var(--spacing-scale-096))] md:px-[calc(var(--spacing-scale-096)+var(--spacing-scale-096))] lg:px-[calc(var(--spacing-scale-096)+var(--spacing-scale-096))] xl:px-[calc(var(--spacing-scale-160)+var(--spacing-scale-096))]"
|
: "px-[var(--spacing-scale-032)] md:px-[calc(var(--spacing-scale-096)+var(--spacing-scale-096))] lg:px-[calc(var(--spacing-scale-096)+var(--spacing-scale-096))] xl:px-[calc(var(--spacing-scale-160)+var(--spacing-scale-096))]"
|
||||||
} ${className}`.trim()}
|
} ${className}`.trim()}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -131,7 +164,7 @@ function TripleTextBlockView({
|
|||||||
className={
|
className={
|
||||||
isUseCases
|
isUseCases
|
||||||
? "flex w-full flex-col gap-[var(--spacing-scale-048)] lg:flex-row lg:items-start lg:gap-[var(--spacing-scale-032)]"
|
? "flex w-full flex-col gap-[var(--spacing-scale-048)] lg:flex-row lg:items-start lg:gap-[var(--spacing-scale-032)]"
|
||||||
: "flex w-full flex-col gap-[var(--spacing-scale-032)] lg:flex-row lg:items-start lg:gap-[var(--spacing-scale-032)]"
|
: "flex w-full flex-col gap-[var(--spacing-scale-048)] lg:flex-row lg:items-start lg:gap-[var(--spacing-scale-032)]"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{columns.map((column, index) => (
|
{columns.map((column, index) => (
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe("TripleTextBlock", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole("heading", { name: "Stacked headline" }),
|
screen.getByRole("heading", { level: 3, name: "Stacked headline" }),
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole("heading", { name: "Wide headline" }),
|
screen.getByRole("heading", { name: "Wide headline" }),
|
||||||
@@ -44,9 +44,32 @@ describe("TripleTextBlock", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getAllByRole("heading", { name: "Only headline" })).toHaveLength(
|
expect(screen.getAllByRole("heading", { name: "Only headline" })).toHaveLength(
|
||||||
1,
|
2,
|
||||||
);
|
);
|
||||||
expect(screen.getByText("Only body.")).toBeInTheDocument();
|
const stackedHeading = screen.getByRole("heading", {
|
||||||
|
level: 3,
|
||||||
|
name: "Only headline",
|
||||||
|
});
|
||||||
|
expect(stackedHeading).toHaveClass("text-[24px]");
|
||||||
|
expect(screen.getAllByText("Only body.")).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("default preset uses use-cases-matched baseline horizontal padding", () => {
|
||||||
|
const { container } = render(
|
||||||
|
<TripleTextBlock
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: "Only headline",
|
||||||
|
description: "Only body.",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const section = container.querySelector("section");
|
||||||
|
expect(section).toBeTruthy();
|
||||||
|
expect(section).toHaveClass("px-[var(--spacing-scale-032)]");
|
||||||
|
expect(section).not.toHaveClass("px-[calc(var(--spacing-scale-032)+var(--spacing-scale-096))]");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("useCases preset renders persistent section heading, column h3 titles, dual paragraphs, outline CTA", () => {
|
it("useCases preset renders persistent section heading, column h3 titles, dual paragraphs, outline CTA", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user