QA pass: layout, create-flow, and About books #68
@@ -153,21 +153,17 @@ export default function LegalDocumentPage({
|
||||
/>
|
||||
|
||||
<div className="relative min-h-screen overflow-x-hidden bg-transparent">
|
||||
<ContentBanner post={post} variant="guide" />
|
||||
<ContentBanner
|
||||
post={post}
|
||||
variant="guide"
|
||||
updatedLabel={page.updated}
|
||||
/>
|
||||
|
||||
<article
|
||||
className="relative z-10 p-[var(--spacing-scale-024)] sm:py-[var(--spacing-scale-032)]"
|
||||
className="relative z-10 flex w-full justify-center p-[var(--spacing-scale-024)] sm:px-0 sm:py-[var(--spacing-scale-032)]"
|
||||
data-node-id="19003:23574"
|
||||
>
|
||||
<div className={`${ARTICLE_COLUMN_CLASS} -mt-[var(--spacing-scale-048)]`}>
|
||||
<div
|
||||
className="flex flex-wrap items-end gap-[var(--measures-spacing-008,8px)] text-xx-small-paragraph text-[var(--color-content-default-secondary)] md:text-x-small-paragraph lg:text-small-paragraph xl:text-large-paragraph"
|
||||
data-name="Metadata Container"
|
||||
>
|
||||
<span>{page.banner.author}</span>
|
||||
<span>{page.updated}</span>
|
||||
</div>
|
||||
|
||||
<div className={ARTICLE_COLUMN_CLASS}>
|
||||
{page.intro.length > 0 ? (
|
||||
<div className="flex flex-col gap-[1em]">
|
||||
{page.intro.map((paragraph) => (
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function HowItWorksPage() {
|
||||
|
||||
<article className="relative z-10 p-[var(--spacing-scale-024)] sm:py-[var(--spacing-scale-032)]">
|
||||
<div
|
||||
className="post-body -mt-[var(--spacing-scale-048)] text-[var(--color-content-default-primary)] text-medium-paragraph sm:text-large-paragraph lg:text-x-large-paragraph xl:text-xx-large-paragraph sm:mx-auto sm:max-w-[390px] md:max-w-[472px] lg:max-w-[700px] xl:max-w-[904px]"
|
||||
className="post-body text-[var(--color-content-default-primary)] text-medium-paragraph sm:text-large-paragraph lg:text-x-large-paragraph xl:text-xx-large-paragraph sm:mx-auto sm:max-w-[390px] md:max-w-[472px] lg:max-w-[700px] xl:max-w-[904px]"
|
||||
dangerouslySetInnerHTML={{ __html: syntheticPost.htmlContent }}
|
||||
/>
|
||||
</article>
|
||||
|
||||
@@ -22,6 +22,7 @@ const ContentBannerContainer = memo<ContentBannerProps>(
|
||||
leadingImageAlt,
|
||||
rulePreview,
|
||||
contentTone,
|
||||
updatedLabel,
|
||||
}) => {
|
||||
const variant = variantProp;
|
||||
const tUseCase = useTranslation("pages.useCasesCompletedRule");
|
||||
@@ -81,6 +82,7 @@ const ContentBannerContainer = memo<ContentBannerProps>(
|
||||
rulePreview={rulePreview}
|
||||
contentTone={contentTone}
|
||||
ruleCardLinkAriaLabel={ruleCardLinkAriaLabel}
|
||||
updatedLabel={updatedLabel}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -28,6 +28,8 @@ export interface ContentBannerProps {
|
||||
rulePreview?: ContentBannerRulePreview;
|
||||
/** `useCase` only: ContentContainer text tokens (default `onLight`). */
|
||||
contentTone?: ContentContainerToneValue;
|
||||
/** `guide` only: author + this label under the title. */
|
||||
updatedLabel?: string;
|
||||
}
|
||||
|
||||
export interface ContentBannerViewProps {
|
||||
@@ -43,4 +45,6 @@ export interface ContentBannerViewProps {
|
||||
contentTone?: ContentContainerToneValue;
|
||||
/** `useCase` only: aria-label for linked rule preview. */
|
||||
ruleCardLinkAriaLabel?: string;
|
||||
/** `guide` only: attribution row under the title. */
|
||||
updatedLabel?: string;
|
||||
}
|
||||
|
||||
@@ -17,23 +17,23 @@ import type { ContentBannerViewProps } from "./ContentBanner.types";
|
||||
*/
|
||||
function ContentBannerGuideView({
|
||||
post,
|
||||
}: Pick<ContentBannerViewProps, "post">) {
|
||||
const { title, description } = post.frontmatter;
|
||||
updatedLabel,
|
||||
}: Pick<ContentBannerViewProps, "post" | "updatedLabel">) {
|
||||
const { title, description, author } = post.frontmatter;
|
||||
|
||||
return (
|
||||
<section
|
||||
className="relative w-full overflow-clip px-[var(--spacing-scale-020)] py-[var(--spacing-scale-024)] sm:px-[var(--spacing-scale-032)] sm:py-[var(--spacing-scale-032)] lg:px-[var(--spacing-scale-048)] lg:py-[var(--spacing-scale-040)]"
|
||||
className="relative w-full overflow-clip px-[var(--spacing-scale-020)] py-[var(--spacing-scale-024)] sm:px-[var(--spacing-scale-032)] sm:py-[var(--spacing-scale-032)] lg:px-[var(--spacing-scale-048)] lg:py-[var(--spacing-scale-040)] xl:px-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-076)]"
|
||||
aria-labelledby="content-banner-title"
|
||||
>
|
||||
<div
|
||||
className="mx-auto flex w-full max-w-[1024px] flex-col items-start gap-[var(--spacing-scale-024)] md:flex-row md:items-center md:gap-[var(--spacing-scale-032)]"
|
||||
className="mx-auto flex w-full max-w-[1024px] flex-col items-start gap-[var(--spacing-scale-024)] md:flex-row md:items-start md:gap-[var(--spacing-scale-024)] lg:items-center lg:gap-[var(--spacing-scale-032)] xl:max-w-[1312px] xl:gap-[var(--spacing-scale-056)]"
|
||||
data-node-id="19189:9358"
|
||||
>
|
||||
<div
|
||||
className="flex w-full max-w-[365px] shrink-0 flex-col items-start justify-center gap-[var(--spacing-scale-024)]"
|
||||
className="flex w-full shrink-0 flex-col items-start justify-center gap-[var(--measures-spacing-016)] md:max-w-[280px] lg:max-w-[365px] xl:max-w-[623px]"
|
||||
data-node-id="19189:9171"
|
||||
>
|
||||
<div className="flex w-full flex-col items-start gap-[var(--measures-spacing-016)]">
|
||||
<div className="flex w-full flex-col items-start gap-[var(--measures-spacing-004)] text-left text-[var(--color-content-default-primary)]">
|
||||
<h1
|
||||
id="content-banner-title"
|
||||
@@ -47,11 +47,19 @@ function ContentBannerGuideView({
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
{updatedLabel ? (
|
||||
<div
|
||||
className="flex w-full flex-wrap items-end gap-[var(--measures-spacing-008,8px)] text-xx-small-paragraph text-[var(--color-content-default-secondary)] md:text-x-small-paragraph lg:text-small-paragraph xl:text-large-paragraph"
|
||||
data-name="Metadata Container"
|
||||
>
|
||||
<span>{author}</span>
|
||||
<span>{updatedLabel}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="flex w-full shrink-0 items-center justify-center md:flex-1"
|
||||
className="flex w-full shrink-0 items-center justify-center max-sm:pb-[var(--spacing-scale-024)] md:flex-1 md:justify-end"
|
||||
data-node-id="22078:806960"
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
@@ -253,7 +261,7 @@ function ContentBannerUseCaseView({
|
||||
|
||||
function ContentBannerView(props: ContentBannerViewProps) {
|
||||
if (props.variant === "guide") {
|
||||
return <ContentBannerGuideView post={props.post} />;
|
||||
return <ContentBannerGuideView post={props.post} updatedLabel={props.updatedLabel} />;
|
||||
}
|
||||
|
||||
if (props.variant === "useCase") {
|
||||
|
||||
@@ -179,15 +179,44 @@ describe("ContentBanner", () => {
|
||||
expect(screen.getByText("Test description")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Test Author")).not.toBeInTheDocument();
|
||||
|
||||
const copyColumn = container.querySelector('[data-node-id="19189:9171"]');
|
||||
expect(copyColumn).toHaveClass(
|
||||
"md:max-w-[280px]",
|
||||
"lg:max-w-[365px]",
|
||||
"xl:max-w-[623px]",
|
||||
);
|
||||
expect(copyColumn).not.toHaveClass("max-w-[365px]");
|
||||
|
||||
const bannerRow = container.querySelector('[data-node-id="19189:9358"]');
|
||||
expect(bannerRow).toHaveClass("md:flex-row");
|
||||
expect(bannerRow?.className).toMatch(/xl:max-w-\[1312px\]/);
|
||||
|
||||
const logoMark = container.querySelector(
|
||||
'[data-node-id="22078:806960"] img',
|
||||
const logoMarkWrap = container.querySelector(
|
||||
'[data-node-id="22078:806960"]',
|
||||
);
|
||||
expect(logoMarkWrap?.className).toMatch(
|
||||
/max-sm:pb-\[var\(--spacing-scale-024\)\]/,
|
||||
);
|
||||
expect(logoMarkWrap).toHaveClass("md:justify-end");
|
||||
const logoMark = logoMarkWrap?.querySelector("img");
|
||||
expect(logoMark).toHaveAttribute(
|
||||
"src",
|
||||
expect.stringContaining("guide-banner-logo-arrow.svg"),
|
||||
);
|
||||
});
|
||||
|
||||
it("renders guide metadata under the title when updatedLabel is set", () => {
|
||||
render(
|
||||
<ContentBanner
|
||||
post={mockPost}
|
||||
variant="guide"
|
||||
updatedLabel="Last updated August 15, 2026."
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("Test Author")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Last updated August 15, 2026."),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,10 +7,16 @@ import CookiesPage from "../../app/(marketing)/cookies/page";
|
||||
import messages from "../../messages/en/index";
|
||||
|
||||
vi.mock("../../app/components/sections/ContentBanner", () => ({
|
||||
default: ({ post, variant }) => (
|
||||
default: ({ post, variant, updatedLabel }) => (
|
||||
<section data-testid="content-banner" data-variant={variant}>
|
||||
<h1>{post.frontmatter.title}</h1>
|
||||
<p>{post.frontmatter.description}</p>
|
||||
{updatedLabel ? (
|
||||
<div>
|
||||
<span>{post.frontmatter.author}</span>
|
||||
<span>{updatedLabel}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user