"use client" import React, { useRef, useState, useEffect, useCallback } from 'react' import { Chevron02DownIcon } from './icons-v2-generated/arrows/chevron-02-down-icon' import { cn } from "../utils/cn" import { faqItemAnchor } from "../utils/faq-anchor" export interface FaqItem { id: number | string question: string answer: string } interface FaqAccordionProps { items: FaqItem[] defaultOpenIds?: (number | string)[] } // Utility to measure scrollHeight outside render cycle const useMeasuredHeight = (isOpen: boolean) => { const ref = useRef(null) const [maxHeight, setMaxHeight] = useState('0px') const measure = useCallback(() => { if (ref.current) { const height = ref.current.scrollHeight setMaxHeight(`${height}px`) } }, []) // Update height only when section is open useEffect(() => { if (isOpen) { measure() } else { setMaxHeight('0px') } }, [isOpen, measure]) return { ref, maxHeight } } export function FaqAccordion({ items, defaultOpenIds = [] }: FaqAccordionProps) { const [openSet, setOpenSet] = useState>(new Set(defaultOpenIds)) const toggle = (id: string | number) => { setOpenSet(prev => { const next = new Set(prev) if (next.has(id)) next.delete(id) else next.add(id) return next }) } return (
{items.map(item => { const isOpen = openSet.has(item.id) const { ref, maxHeight } = useMeasuredHeight(isOpen) return (
`) land // here via native browser hash scroll AND via `FaqSection`'s tween // dispatch. `scroll-mt-24` keeps the row header below the 96px // sticky nav offset (matches `
`'s scroll-margin for // category anchors). id={faqItemAnchor(item.id)} className="scroll-mt-24 transition-colors hover:bg-ods-bg-hover" > {/* Header */}
toggle(item.id)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(item.id); } }} aria-expanded={isOpen} className="flex w-full items-center gap-6 md:gap-10 px-6 py-4 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-focus transition-colors cursor-pointer" >

{item.question}

{/* Content wrapper with max-height animation */}
{/* break-words: FAQ answers render as plain text, so a long URL or token has no wrap opportunity — and the parent is overflow-hidden, which would CLIP it past the viewport on mobile. Mirrors the markdown-renderer overflow-wrap fix. */}
{item.answer}
) })}
) }