Cleanup pass 2
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Figma: "Sections / AskOrganizer" (see registry).
|
||||
*/
|
||||
import { memo, useCallback, useState } from "react";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import { useAnalytics } from "../../../hooks";
|
||||
@@ -43,7 +46,6 @@ const AskOrganizerContainer = memo<AskOrganizerProps>(
|
||||
subtitle,
|
||||
description,
|
||||
buttonText,
|
||||
buttonHref,
|
||||
className = "",
|
||||
variant: variantProp = "centered",
|
||||
onContactClick,
|
||||
@@ -51,7 +53,7 @@ const AskOrganizerContainer = memo<AskOrganizerProps>(
|
||||
const variant = variantProp;
|
||||
const t = useTranslation();
|
||||
const defaultButtonText = buttonText ?? t("askOrganizer.buttonText");
|
||||
const analyticsHref = buttonHref ?? "modal";
|
||||
const ctaAriaLabel = t("askOrganizer.ariaLabel");
|
||||
const { trackEvent, trackCustomEvent } = useAnalytics();
|
||||
const [inquiryOpen, setInquiryOpen] = useState(false);
|
||||
|
||||
@@ -74,11 +76,9 @@ const AskOrganizerContainer = memo<AskOrganizerProps>(
|
||||
|
||||
const labelledBy = title ? "ask-organizer-headline" : undefined;
|
||||
|
||||
const handleContactClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>,
|
||||
) => {
|
||||
if (buttonHref) {
|
||||
// Legacy link CTA: do not intercept navigation.
|
||||
const handleContactClick = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
trackEvent({
|
||||
event: "contact_button_click",
|
||||
category: "engagement",
|
||||
@@ -86,46 +86,30 @@ const AskOrganizerContainer = memo<AskOrganizerProps>(
|
||||
component: "AskOrganizer",
|
||||
variant: resolvedVariant,
|
||||
});
|
||||
|
||||
trackCustomEvent(
|
||||
"contact_button_click",
|
||||
{
|
||||
component: "AskOrganizer",
|
||||
variant: resolvedVariant,
|
||||
buttonText: defaultButtonText,
|
||||
buttonHref: analyticsHref,
|
||||
buttonHref: "modal",
|
||||
},
|
||||
onContactClick as
|
||||
| ((_data: Record<string, unknown>) => void)
|
||||
| undefined,
|
||||
);
|
||||
return event;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
trackEvent({
|
||||
event: "contact_button_click",
|
||||
category: "engagement",
|
||||
label: "ask_organizer",
|
||||
component: "AskOrganizer",
|
||||
variant: resolvedVariant,
|
||||
});
|
||||
|
||||
trackCustomEvent(
|
||||
"contact_button_click",
|
||||
{
|
||||
component: "AskOrganizer",
|
||||
variant: resolvedVariant,
|
||||
buttonText: defaultButtonText,
|
||||
buttonHref: analyticsHref,
|
||||
},
|
||||
onContactClick as
|
||||
| ((_data: Record<string, unknown>) => void)
|
||||
| undefined,
|
||||
);
|
||||
|
||||
setInquiryOpen(true);
|
||||
return event;
|
||||
};
|
||||
setInquiryOpen(true);
|
||||
},
|
||||
[
|
||||
defaultButtonText,
|
||||
onContactClick,
|
||||
resolvedVariant,
|
||||
trackCustomEvent,
|
||||
trackEvent,
|
||||
],
|
||||
);
|
||||
|
||||
const closeInquiry = useCallback(() => {
|
||||
setInquiryOpen(false);
|
||||
@@ -138,7 +122,7 @@ const AskOrganizerContainer = memo<AskOrganizerProps>(
|
||||
subtitle={subtitle}
|
||||
description={description}
|
||||
buttonText={defaultButtonText}
|
||||
buttonHref={buttonHref}
|
||||
ctaAriaLabel={ctaAriaLabel}
|
||||
className={className}
|
||||
sectionPadding={sectionPadding}
|
||||
contentGap={`${contentGap} ${styles.container}`}
|
||||
|
||||
@@ -12,10 +12,6 @@ export interface AskOrganizerProps {
|
||||
subtitle?: string;
|
||||
description?: string;
|
||||
buttonText?: string;
|
||||
/**
|
||||
* @deprecated Modal-only flow (CR-107). Omit; kept optional for Storybook overrides.
|
||||
*/
|
||||
buttonHref?: string;
|
||||
className?: string;
|
||||
/**
|
||||
* Ask organizer variant.
|
||||
@@ -26,7 +22,6 @@ export interface AskOrganizerProps {
|
||||
component: string;
|
||||
variant: string;
|
||||
buttonText: string;
|
||||
buttonHref?: string;
|
||||
timestamp: string;
|
||||
}) => void;
|
||||
}
|
||||
@@ -36,7 +31,7 @@ export interface AskOrganizerViewProps {
|
||||
subtitle?: string;
|
||||
description?: string;
|
||||
buttonText: string;
|
||||
buttonHref?: string;
|
||||
ctaAriaLabel: string;
|
||||
className: string;
|
||||
sectionPadding: string;
|
||||
contentGap: string;
|
||||
@@ -44,6 +39,6 @@ export interface AskOrganizerViewProps {
|
||||
variant: AskOrganizerVariant;
|
||||
labelledBy?: string;
|
||||
onContactClick: (
|
||||
_event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>,
|
||||
_event: React.MouseEvent<HTMLButtonElement>,
|
||||
) => void;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import ContentLockup from "../../type/ContentLockup";
|
||||
import Button from "../../buttons/Button";
|
||||
import type { AskOrganizerViewProps } from "./AskOrganizer.types";
|
||||
@@ -10,7 +9,7 @@ function AskOrganizerView({
|
||||
subtitle,
|
||||
description,
|
||||
buttonText,
|
||||
buttonHref,
|
||||
ctaAriaLabel,
|
||||
className,
|
||||
sectionPadding,
|
||||
contentGap,
|
||||
@@ -19,8 +18,6 @@ function AskOrganizerView({
|
||||
labelledBy,
|
||||
onContactClick,
|
||||
}: AskOrganizerViewProps) {
|
||||
const t = useTranslation();
|
||||
const ariaLabel = t("askOrganizer.ariaLabel");
|
||||
const isUseCaseDetail = variant === "use-case-detail";
|
||||
const lockupVariant =
|
||||
variant === "inverse" || isUseCaseDetail ? "ask-inverse" : "ask";
|
||||
@@ -33,14 +30,13 @@ function AskOrganizerView({
|
||||
<section
|
||||
className={`${sectionPadding} ${className}`}
|
||||
aria-labelledby={labelledBy}
|
||||
aria-label={labelledBy ? undefined : ariaLabel}
|
||||
aria-label={labelledBy ? undefined : ctaAriaLabel}
|
||||
tabIndex={-1}
|
||||
data-figma-node={isUseCaseDetail ? "22015-42624" : "18116-15960"}
|
||||
>
|
||||
<div
|
||||
className={`mx-auto flex w-full min-w-0 max-w-[1280px] flex-col md:min-w-[358px] ${contentGap} ${isUseCaseDetail ? "items-center" : ""}`}
|
||||
>
|
||||
{/* Content Lockup */}
|
||||
<ContentLockup
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
@@ -50,18 +46,16 @@ function AskOrganizerView({
|
||||
titleId={labelledBy}
|
||||
/>
|
||||
|
||||
{/* Button */}
|
||||
<div
|
||||
className={`${buttonContainerClass} flex-wrap gap-y-[var(--spacing-scale-016)]`}
|
||||
>
|
||||
<Button
|
||||
{...(buttonHref ? { href: buttonHref } : {})}
|
||||
size="small"
|
||||
buttonType="filled"
|
||||
palette={buttonPalette}
|
||||
className="!px-[var(--spacing-scale-010)] md:!px-[var(--spacing-scale-016)] md:!py-[var(--spacing-scale-012)] md:!text-[16px] md:!leading-[20px]"
|
||||
onClick={onContactClick}
|
||||
ariaLabel={ariaLabel}
|
||||
ariaLabel={ctaAriaLabel}
|
||||
data-testid="ask-organizer-cta"
|
||||
>
|
||||
{buttonText}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import {
|
||||
ASSETS,
|
||||
getAssetPath,
|
||||
@@ -23,6 +24,14 @@ const ContentBannerContainer = memo<ContentBannerProps>(
|
||||
contentTone,
|
||||
}) => {
|
||||
const variant = variantProp;
|
||||
const tUseCase = useTranslation("pages.useCasesCompletedRule");
|
||||
const ruleCardLinkAriaLabel =
|
||||
variant === "useCase" && rulePreview?.href
|
||||
? tUseCase("ruleCardLinkAriaLabel").replace(
|
||||
"{title}",
|
||||
rulePreview.title,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const resolveHorizontalImage = (blogPost: BlogPost): string => {
|
||||
if (blogPost.frontmatter?.thumbnail?.horizontal) {
|
||||
@@ -71,6 +80,7 @@ const ContentBannerContainer = memo<ContentBannerProps>(
|
||||
backgroundImageSection={backgroundImageSection}
|
||||
rulePreview={rulePreview}
|
||||
contentTone={contentTone}
|
||||
ruleCardLinkAriaLabel={ruleCardLinkAriaLabel}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -41,4 +41,6 @@ export interface ContentBannerViewProps {
|
||||
backgroundImageSection?: string;
|
||||
rulePreview?: ContentBannerRulePreview;
|
||||
contentTone?: ContentContainerToneValue;
|
||||
/** `useCase` only: aria-label for linked rule preview. */
|
||||
ruleCardLinkAriaLabel?: string;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { memo } from "react";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import ContentContainer from "../../content/ContentContainer";
|
||||
import Rule from "../../cards/Rule";
|
||||
import {
|
||||
@@ -155,6 +154,7 @@ function ContentBannerUseCaseView({
|
||||
contentTone = "inverse",
|
||||
leadingImageSrc,
|
||||
leadingImageAlt,
|
||||
ruleCardLinkAriaLabel,
|
||||
}: Pick<
|
||||
ContentBannerViewProps,
|
||||
| "post"
|
||||
@@ -162,8 +162,8 @@ function ContentBannerUseCaseView({
|
||||
| "contentTone"
|
||||
| "leadingImageSrc"
|
||||
| "leadingImageAlt"
|
||||
| "ruleCardLinkAriaLabel"
|
||||
>) {
|
||||
const t = useTranslation("pages.useCasesCompletedRule");
|
||||
if (!rulePreview) {
|
||||
return null;
|
||||
}
|
||||
@@ -198,10 +198,7 @@ function ContentBannerUseCaseView({
|
||||
<Link
|
||||
href={rulePreview.href}
|
||||
className="block w-full rounded-[24px] outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-default-brand-primary)] focus-visible:ring-offset-2"
|
||||
aria-label={t("ruleCardLinkAriaLabel").replace(
|
||||
"{title}",
|
||||
rulePreview.title,
|
||||
)}
|
||||
aria-label={ruleCardLinkAriaLabel ?? rulePreview.title}
|
||||
>
|
||||
<Rule
|
||||
title={rulePreview.title}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import { logger } from "../../../../lib/logger";
|
||||
import { prepareFreshCreateFlowEntry } from "../../../(app)/create/utils/prepareFreshCreateFlowEntry";
|
||||
import {
|
||||
@@ -34,6 +35,8 @@ declare global {
|
||||
const RuleStackContainer = memo<RuleStackProps>(
|
||||
({ className = "", initialGridEntries, translationNamespace, twoColumnsFromMd }) => {
|
||||
const router = useRouter();
|
||||
const namespace = translationNamespace ?? "pages.home.ruleStack";
|
||||
const t = useTranslation(namespace);
|
||||
const [gridEntries, setGridEntries] = useState<TemplateGridCardEntry[] | null>(
|
||||
() => initialGridEntries ?? null,
|
||||
);
|
||||
@@ -107,7 +110,9 @@ const RuleStackContainer = memo<RuleStackProps>(
|
||||
className={className}
|
||||
onTemplateClick={handleTemplateClick}
|
||||
gridEntries={gridEntries}
|
||||
translationNamespace={translationNamespace ?? "pages.home.ruleStack"}
|
||||
sectionTitle={t("title")}
|
||||
sectionSubtitle={t("subtitle")}
|
||||
seeAllTemplatesLabel={t("button.seeAllTemplates")}
|
||||
twoColumnsFromMd={twoColumnsFromMd}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,8 @@ export interface RuleStackViewProps {
|
||||
onTemplateClick: (_slug: string) => void;
|
||||
/** `null` while loading curated templates from the API. */
|
||||
gridEntries: TemplateGridCardEntry[] | null;
|
||||
translationNamespace: string;
|
||||
sectionTitle: string;
|
||||
sectionSubtitle: string;
|
||||
seeAllTemplatesLabel: string;
|
||||
twoColumnsFromMd?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import SectionHeader from "../../type/SectionHeader";
|
||||
import Button from "../../buttons/Button";
|
||||
import { GovernanceTemplateGrid } from "../GovernanceTemplateGrid";
|
||||
@@ -12,12 +11,11 @@ export function RuleStackView({
|
||||
className,
|
||||
onTemplateClick,
|
||||
gridEntries,
|
||||
translationNamespace,
|
||||
sectionTitle,
|
||||
sectionSubtitle,
|
||||
seeAllTemplatesLabel,
|
||||
twoColumnsFromMd = false,
|
||||
}: RuleStackViewProps) {
|
||||
const t = useTranslation(translationNamespace);
|
||||
const buttonText = t("button.seeAllTemplates");
|
||||
|
||||
return (
|
||||
<section
|
||||
data-figma-node="22085-860413"
|
||||
@@ -35,8 +33,8 @@ export function RuleStackView({
|
||||
`}
|
||||
>
|
||||
<SectionHeader
|
||||
title={t("title")}
|
||||
subtitle={t("subtitle")}
|
||||
title={sectionTitle}
|
||||
subtitle={sectionSubtitle}
|
||||
variant="multi-line"
|
||||
ruleStackDesktopTypeScale
|
||||
twoColumnsFromMd={twoColumnsFromMd}
|
||||
@@ -69,7 +67,7 @@ export function RuleStackView({
|
||||
size="large"
|
||||
href="/templates"
|
||||
>
|
||||
{buttonText}
|
||||
{seeAllTemplatesLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user