Backend / staging cleanup, performance substrate, and create-flow polish #60

Merged
an.di merged 16 commits from adilallo/Backend/StagingCleanup into main 2026-05-26 15:11:47 +00:00
10 changed files with 57 additions and 12 deletions
Showing only changes of commit d8e0b87bb3 - Show all commits
+2 -2
View File
@@ -3,7 +3,7 @@ blank_issues_enabled: false
contact_links: contact_links:
- name: Open the staging site - name: Open the staging site
url: https://staging.communityrule.info url: https://staging.communityrule.info
about: Preview version of Community Rule test here before reporting about: Preview version of Community Rule, test here before reporting
- name: How to sign in (read first) - name: How to sign in (read first)
url: https://staging.communityrule.info/login url: https://staging.communityrule.info/login
about: Use your email — we send a link to click, not a code to type about: Use your email to recieve a link to click
@@ -14,6 +14,7 @@ import { useTranslation } from "../../../contexts/MessagesContext";
const AskOrganizerInquiryModalContainer = memo<AskOrganizerInquiryModalProps>( const AskOrganizerInquiryModalContainer = memo<AskOrganizerInquiryModalProps>(
({ isOpen, onClose }) => { ({ isOpen, onClose }) => {
const t = useTranslation("modals.askOrganizerInquiry"); const t = useTranslation("modals.askOrganizerInquiry");
const tLogin = useTranslation("pages.login");
const copy = useMemo( const copy = useMemo(
() => ({ () => ({
title: t("title"), title: t("title"),
@@ -28,8 +29,9 @@ const AskOrganizerInquiryModalContainer = memo<AskOrganizerInquiryModalProps>(
successDescription: t("successDescription"), successDescription: t("successDescription"),
ariaDialog: t("ariaDialog"), ariaDialog: t("ariaDialog"),
honeypotLabel: t("honeypotLabel"), honeypotLabel: t("honeypotLabel"),
backToHome: tLogin("backToHome"),
}), }),
[t], [t, tLogin],
); );
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
@@ -16,6 +16,7 @@ export interface AskOrganizerInquiryModalCopy {
successDescription: string; successDescription: string;
ariaDialog: string; ariaDialog: string;
honeypotLabel: string; honeypotLabel: string;
backToHome: string;
} }
export interface AskOrganizerInquiryModalViewProps export interface AskOrganizerInquiryModalViewProps
@@ -1,5 +1,6 @@
"use client"; "use client";
import Link from "next/link";
import Create from "../Create"; import Create from "../Create";
import TextInput from "../../controls/TextInput"; import TextInput from "../../controls/TextInput";
import TextArea from "../../controls/TextArea"; import TextArea from "../../controls/TextArea";
@@ -72,6 +73,15 @@ export function AskOrganizerInquiryModalView({
ariaLabel={copy.ariaDialog} ariaLabel={copy.ariaDialog}
footerContent={footer} footerContent={footer}
footerClassName="!h-auto min-h-[112px] shrink-0 flex flex-col justify-end pb-8 pt-3 px-4" footerClassName="!h-auto min-h-[112px] shrink-0 flex flex-col justify-end pb-8 pt-3 px-4"
belowCard={
<Link
href="/"
className="font-inter font-normal text-[14px] leading-[20px] text-[var(--color-content-invert-tertiary,#2d2d2d)] text-center hover:opacity-90"
onClick={() => onClose()}
>
{copy.backToHome}
</Link>
}
> >
{success ? ( {success ? (
<div className="flex flex-col gap-3 py-2"> <div className="flex flex-col gap-3 py-2">
@@ -36,6 +36,7 @@ const CreateContainer = memo<CreateProps>(
kebabTriggerAriaLabel, kebabTriggerAriaLabel,
kebabMenuAriaLabel, kebabMenuAriaLabel,
kebabMenuItems, kebabMenuItems,
belowCard,
}) => { }) => {
const createRef = useRef<HTMLDivElement>(null); const createRef = useRef<HTMLDivElement>(null);
const overlayRef = useRef<HTMLDivElement>(null); const overlayRef = useRef<HTMLDivElement>(null);
@@ -72,6 +73,7 @@ const CreateContainer = memo<CreateProps>(
kebabTriggerAriaLabel={kebabTriggerAriaLabel} kebabTriggerAriaLabel={kebabTriggerAriaLabel}
kebabMenuAriaLabel={kebabMenuAriaLabel} kebabMenuAriaLabel={kebabMenuAriaLabel}
kebabMenuItems={kebabMenuItems} kebabMenuItems={kebabMenuItems}
belowCard={belowCard}
/> />
); );
}, },
@@ -43,6 +43,8 @@ export interface CreateProps {
kebabTriggerAriaLabel?: string; kebabTriggerAriaLabel?: string;
kebabMenuAriaLabel?: string; kebabMenuAriaLabel?: string;
kebabMenuItems?: ModalHeaderMenuItem[]; kebabMenuItems?: ModalHeaderMenuItem[];
/** Rendered below the dialog card on the backdrop (e.g. “Back to home”). */
belowCard?: React.ReactNode;
} }
export interface CreateViewProps { export interface CreateViewProps {
@@ -73,4 +75,5 @@ export interface CreateViewProps {
kebabTriggerAriaLabel?: string; kebabTriggerAriaLabel?: string;
kebabMenuAriaLabel?: string; kebabMenuAriaLabel?: string;
kebabMenuItems?: ModalHeaderMenuItem[]; kebabMenuItems?: ModalHeaderMenuItem[];
belowCard?: React.ReactNode;
} }
@@ -34,6 +34,7 @@ export function CreateView({
kebabTriggerAriaLabel, kebabTriggerAriaLabel,
kebabMenuAriaLabel, kebabMenuAriaLabel,
kebabMenuItems, kebabMenuItems,
belowCard,
}: CreateViewProps) { }: CreateViewProps) {
return ( return (
<CreateModalFrameView <CreateModalFrameView
@@ -45,6 +46,7 @@ export function CreateView({
ariaLabelledBy={ariaLabelledBy} ariaLabelledBy={ariaLabelledBy}
overlayRef={overlayRef} overlayRef={overlayRef}
dialogRef={createRef} dialogRef={createRef}
belowCard={belowCard}
> >
<ModalHeader <ModalHeader
onClose={onClose} onClose={onClose}
@@ -22,6 +22,8 @@ export type CreateModalFrameViewProps = {
overlayRef: RefObject<HTMLDivElement | null>; overlayRef: RefObject<HTMLDivElement | null>;
dialogRef: RefObject<HTMLDivElement | null>; dialogRef: RefObject<HTMLDivElement | null>;
children: ReactNode; children: ReactNode;
/** Rendered below the dialog card on the backdrop (e.g. “Back to home”). */
belowCard?: ReactNode;
}; };
/** /**
@@ -37,28 +39,34 @@ export function CreateModalFrameView({
overlayRef, overlayRef,
dialogRef, dialogRef,
children, children,
belowCard,
}: CreateModalFrameViewProps) { }: CreateModalFrameViewProps) {
if (!isOpen) return null; if (!isOpen) return null;
const content = ( const content = (
<> <div
<div ref={overlayRef}
ref={overlayRef} className={`${backdropOverlayClasses[backdropVariant]} flex flex-col items-center justify-center gap-6 overflow-y-auto px-4 py-8`}
className={backdropOverlayClasses[backdropVariant]} onClick={onOverlayClick}
onClick={onOverlayClick} role="presentation"
aria-hidden="true" >
/>
<div <div
ref={dialogRef} ref={dialogRef}
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-label={ariaLabel} aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy} aria-labelledby={ariaLabelledBy}
className={`fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-[var(--color-surface-default-primary)] rounded-[var(--radius-500,20px)] shadow-[0px_0px_48px_0px_rgba(0,0,0,0.1)] w-[560px] max-h-[90vh] flex min-h-0 flex-col overflow-hidden z-[9999] ${className}`} className={`flex min-h-0 max-h-[90vh] w-full max-w-[560px] shrink-0 flex-col overflow-hidden rounded-[var(--radius-500,20px)] bg-[var(--color-surface-default-primary)] shadow-[0px_0px_48px_0px_rgba(0,0,0,0.1)] z-[9999] ${className}`}
onClick={(e) => e.stopPropagation()}
> >
{children} {children}
</div> </div>
</> {belowCard ? (
<div className="shrink-0" onClick={(e) => e.stopPropagation()}>
{belowCard}
</div>
) : null}
</div>
); );
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
+9
View File
@@ -70,6 +70,15 @@ describe("AskOrganizer (behavioral tests)", () => {
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
it("shows back to home link below inquiry modal", async () => {
const user = userEvent.setup();
render(<AskOrganizer title="Test" />);
await user.click(screen.getByTestId("ask-organizer-cta"));
expect(
await screen.findByRole("link", { name: /back to home/i }),
).toHaveAttribute("href", "/");
});
it("renders button with custom text", () => { it("renders button with custom text", () => {
render(<AskOrganizer title="Test" buttonText="Contact" />); render(<AskOrganizer title="Test" buttonText="Contact" />);
expect( expect(
+8
View File
@@ -169,6 +169,14 @@ describe("Create", () => {
expect(screen.getByText("Custom Footer")).toBeInTheDocument(); expect(screen.getByText("Custom Footer")).toBeInTheDocument();
}); });
it("uses responsive width at baseline (matches Login modal)", () => {
renderWithProviders(
<Create {...defaultProps}>Create dialog content</Create>,
);
const dialog = screen.getByRole("dialog");
expect(dialog).toHaveClass("w-full", "max-w-[560px]");
});
it("has proper ARIA attributes", () => { it("has proper ARIA attributes", () => {
renderWithProviders( renderWithProviders(
<Create {...defaultProps} ariaLabel="Test create dialog" />, <Create {...defaultProps} ariaLabel="Test create dialog" />,