diff --git a/app/(dashboard)/events/event-page/EventDetailsClient.tsx b/app/(dashboard)/events/event-page/EventDetailsClient.tsx index 841063d6..4f030cb9 100644 --- a/app/(dashboard)/events/event-page/EventDetailsClient.tsx +++ b/app/(dashboard)/events/event-page/EventDetailsClient.tsx @@ -22,6 +22,8 @@ import { Separator } from "@/components/ui/separator"; import { Clock, DollarSign, Users, BarChart2, Loader2, Share2 } from "lucide-react"; import { formatDistanceToNowStrict, parseISO, isValid } from "date-fns"; import { MarketDetailTabs } from "@/components/market/MarketDetailTabs"; +import { ResolutionPreview } from "@/components/market/ResolutionPreview"; +import { AboutMarketModal } from "@/app/components/AboutMarketModal"; import { ShareSheet } from "@/app/components/ShareSheet"; import { useMediaQuery } from "@/hooks/use-media-query"; import { @@ -222,6 +224,15 @@ export default function EventDetailsClient() { : undefined; const potentialPayout = currentOdds && betAmount ? parseFloat(betAmount || "0") * currentOdds : 0; + const parsedDeadline = parseISO(eventData.deadline); + const deadlineLabel = isValid(parsedDeadline) + ? parsedDeadline.toLocaleDateString(undefined, { + year: "numeric", + month: "short", + day: "numeric", + }) + : "No deadline set"; + const resolutionCriteria = `This market resolves after the betting deadline using verified public outcome sources. The winning outcome must match the final result described by the market premise: ${eventData.description}`; const overviewTab = (
@@ -467,6 +478,13 @@ export default function EventDetailsClient() {
{eventData.category} + + + + + + + About this market + + Review the market premise, key details, and resolution criteria. + + + +

+ Screen reader summary: {marketTitle} is a {category} market. + {deadlineLabel ? ` It closes on ${deadlineLabel}.` : ""}{" "} + {resolutionCriteria} +

+ +
+
+
+ {category} + {deadlineLabel ? ( + + + ) : null} +
+
+

+ {marketTitle} +

+

+ {description} +

+
+
+ +
+
+
+

+ {resolutionCriteria} +

+
+
+
+ + ) +} diff --git a/app/components/__tests__/AboutMarketModal.test.tsx b/app/components/__tests__/AboutMarketModal.test.tsx new file mode 100644 index 00000000..59db24df --- /dev/null +++ b/app/components/__tests__/AboutMarketModal.test.tsx @@ -0,0 +1,31 @@ +import React from "react" +import { render, screen, fireEvent } from "@testing-library/react" +import { AboutMarketModal } from "@/app/components/AboutMarketModal" + +const defaultProps = { + marketTitle: "Super Bowl Winner 2025", + category: "Sports", + description: + "Predict which team will win the Super Bowl LIX scheduled to be played on February 9, 2025.", + resolutionCriteria: + "The market resolves to the official championship winner after the final whistle and source verification.", + deadlineLabel: "Feb 9, 2025", +} + +describe("AboutMarketModal", () => { + it("opens an accessible dialog with market context and resolution criteria", () => { + render() + + fireEvent.click( + screen.getByRole("button", { name: /about this market/i }) + ) + + expect( + screen.getByRole("dialog", { name: /about this market/i }) + ).toBeInTheDocument() + expect(screen.getByText("Super Bowl Winner 2025")).toBeInTheDocument() + expect(screen.getByText("Sports")).toBeInTheDocument() + expect(screen.getAllByText(/official championship winner/i)).toHaveLength(2) + expect(screen.getByText(/screen reader summary/i)).toBeInTheDocument() + }) +}) diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 1fe5c840..80142be9 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -136,7 +136,7 @@ const DialogContentWithFocusReturn = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, children, onOpenAutoFocus, ...props }, ref) => { - const triggerRef = useRef(null); + const triggerRef = React.useRef(null); // Store the trigger element when the dialog opens const handleOpenAutoFocus = (event: Event) => { @@ -197,4 +197,4 @@ export { DialogFooter, DialogTitle, DialogDescription, -}; \ No newline at end of file +}; diff --git a/docs/about-market-modal.md b/docs/about-market-modal.md new file mode 100644 index 00000000..0d558206 --- /dev/null +++ b/docs/about-market-modal.md @@ -0,0 +1,18 @@ +# About Market Modal + +The market detail page includes an `About this market` action beside the market category and share action. It opens `AboutMarketModal`, which summarizes the market premise, category, deadline, and resolution criteria without moving users away from the detail page. + +## Accessibility + +- The trigger is a keyboard-focusable button with a market-specific accessible label. +- The dialog uses Radix Dialog for focus trapping, Escape dismissal, and focus return. +- A screen-reader-only summary connects the dialog content to the market title, category, deadline, and resolution criteria. +- Icons are decorative and marked with `aria-hidden`. + +## Verification + +Run the focused component test: + +```bash +node node_modules/jest/bin/jest.js app/components/__tests__/AboutMarketModal.test.tsx --runInBand +```