Fix ask organizer modal
This commit is contained in:
@@ -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]}
|
className={`${backdropOverlayClasses[backdropVariant]} flex flex-col items-center justify-center gap-6 overflow-y-auto px-4 py-8`}
|
||||||
onClick={onOverlayClick}
|
onClick={onOverlayClick}
|
||||||
aria-hidden="true"
|
role="presentation"
|
||||||
/>
|
>
|
||||||
<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") {
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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" />,
|
||||||
|
|||||||
Reference in New Issue
Block a user