diff --git a/src/app/globals.css b/src/app/globals.css index cd1f838..272d158 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -253,47 +253,16 @@ body.dropdown-open .app-shell { margin 300ms ease-in-out; } -body.question-sheet-open .app-shell .question-detail-header { - max-height: 0; - margin-top: -80px; - transform: translateY(-150px); - opacity: 0; - pointer-events: none; -} - -body.question-sheet-open .app-shell .question-progress { - max-height: 0; - padding-top: 0; - padding-bottom: 0; - opacity: 0; - overflow: hidden; -} - -body.question-sheet-open .app-shell .question-snap-list { - padding-top: 0; - padding-bottom: 0; -} - body.question-keyboard-open .app-shell .question-snap-list { padding-top: 0; padding-bottom: 0; } -body.question-sheet-open .app-shell .question-snap-item[aria-current="step"], body.question-keyboard-open .app-shell .question-snap-item[aria-current="step"] { top: 0; bottom: 0; } -body.question-sheet-open - .app-shell - .question-snap-item[aria-current="step"] - .question-snap-content { - margin-top: 8px; - margin-bottom: 0; - transform: none; -} - body.question-keyboard-open .app-shell .question-snap-item[aria-current="step"] @@ -304,10 +273,6 @@ body.question-keyboard-open transform: translateY(calc(var(--question-keyboard-shift, 0px) * -1)); } -body.question-sheet-open - .app-shell - .question-snap-item[aria-current="step"] - .question-snap-spacer, body.question-keyboard-open .app-shell .question-snap-item[aria-current="step"] diff --git a/src/components/Componentes/question-birthplace.tsx b/src/components/Componentes/question-birthplace.tsx index 706385b..5c1b34f 100644 --- a/src/components/Componentes/question-birthplace.tsx +++ b/src/components/Componentes/question-birthplace.tsx @@ -104,8 +104,6 @@ export function QuestionBirthplace({ const [isDetecting, setIsDetecting] = useState(false); const [detectedLocation, setDetectedLocation] = useState(""); - useSheetScrollLock(isOpen); - useEffect(() => { isMountedRef.current = true; return () => { @@ -131,6 +129,8 @@ export function QuestionBirthplace({ setIsClosing(false); }, [disabled]); + useSheetScrollLock(isOpen, { onBack: closeSheet }); + // Handle escape key useEffect(() => { if (!isOpen) return; diff --git a/src/components/Componentes/question-date-sheet.tsx b/src/components/Componentes/question-date-sheet.tsx index 6b9514e..b07c62f 100644 --- a/src/components/Componentes/question-date-sheet.tsx +++ b/src/components/Componentes/question-date-sheet.tsx @@ -164,13 +164,13 @@ export function QuestionDateSheet({ const [selectedDay, setSelectedDay] = useState(parts[2] || "01"); const dirtyRef = useRef(false); - useSheetScrollLock(true); - const closeSheet = useCallback(() => { setIsClosing(true); window.setTimeout(onClose, EXIT_ANIMATION_MS); }, [onClose]); + useSheetScrollLock(true, { onBack: closeSheet }); + useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { diff --git a/src/components/Componentes/question-phone.tsx b/src/components/Componentes/question-phone.tsx index 72a0a4a..b2a7bd8 100644 --- a/src/components/Componentes/question-phone.tsx +++ b/src/components/Componentes/question-phone.tsx @@ -363,7 +363,7 @@ export function QuestionPhone({ ); }, [countryList, searchQuery]); - useSheetScrollLock(isOpen); + useSheetScrollLock(isOpen, { onBack: closeSheet }); useEffect(() => { if (!isOpen) return; diff --git a/src/components/Componentes/question-sheet.test.tsx b/src/components/Componentes/question-sheet.test.tsx index f3d2581..ce90447 100644 --- a/src/components/Componentes/question-sheet.test.tsx +++ b/src/components/Componentes/question-sheet.test.tsx @@ -181,6 +181,37 @@ describe("QuestionSheet component", () => { }); }); + it("closes the sheet instead of leaving the page on browser Back", async () => { + const question = { + id: "q_back", + title: "کشور", + type: "dropdown", + required: true, + extras: { placeHolder: "انتخاب کشور" }, + options: [{ id: "iran", value: "Iran", label: "ایران", order: 1 }], + ui_config: {}, + } as QuestionField; + const pushState = vi.spyOn(window.history, "pushState"); + + render( + + + + + , + ); + + fireEvent.click(screen.getByRole("button", { name: /انتخاب کشور/i })); + expect(pushState).toHaveBeenCalledTimes(1); + + window.dispatchEvent(new PopStateEvent("popstate")); + + await waitFor(() => { + expect(screen.queryByRole("dialog")).toBeNull(); + }); + pushState.mockRestore(); + }); + it("opens a searchable sheet without focusing search", () => { const question = { id: "q_country", diff --git a/src/components/Componentes/question-sheet.tsx b/src/components/Componentes/question-sheet.tsx index 86efa9d..a54b651 100644 --- a/src/components/Componentes/question-sheet.tsx +++ b/src/components/Componentes/question-sheet.tsx @@ -53,7 +53,7 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { setIsClosing(false); }, [disabled]); - useSheetScrollLock(isOpen); + useSheetScrollLock(isOpen, { onBack: closeSheet }); // Handle escape key useEffect(() => { diff --git a/src/components/Componentes/use-sheet-scroll-lock.ts b/src/components/Componentes/use-sheet-scroll-lock.ts index ffd81f6..bbce203 100644 --- a/src/components/Componentes/use-sheet-scroll-lock.ts +++ b/src/components/Componentes/use-sheet-scroll-lock.ts @@ -1,6 +1,6 @@ "use client"; -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import { viewPaddingsBridge } from "@/lib/view-paddings"; let activeSheetCount = 0; @@ -12,11 +12,35 @@ let initialHtmlOverflow = ""; let initialAppShellOverflow = ""; let initialAppShellTouchAction = ""; let lockedAppShell: HTMLElement | null = null; +const SHEET_HISTORY_KEY = "__habibQuestionSheet"; + +type SheetScrollLockOptions = { + onBack?: () => void; +}; + +export function useSheetScrollLock( + isOpen: boolean, + options: SheetScrollLockOptions = {}, +) { + const { onBack } = options; + const onBackRef = useRef(onBack); + onBackRef.current = onBack; -export function useSheetScrollLock(isOpen: boolean) { useEffect(() => { if (!isOpen) return; + const historyState = window.history.state; + const ownsHistoryEntry = + historyState?.[SHEET_HISTORY_KEY] !== true && activeSheetCount === 0; + + if (ownsHistoryEntry) { + window.history.pushState( + { ...(historyState ?? {}), [SHEET_HISTORY_KEY]: true }, + "", + window.location.href, + ); + } + if (activeSheetCount === 0) { bodyHadDropdownClass = document.body.classList.contains("dropdown-open"); initialBodyOverflow = document.body.style.overflow; @@ -36,9 +60,23 @@ export function useSheetScrollLock(isOpen: boolean) { lockedAppShell.style.touchAction = "none"; } + const handlePopState = () => { + if (ownsHistoryEntry) { + onBackRef.current?.(); + } + }; + window.addEventListener("popstate", handlePopState); + return () => { + window.removeEventListener("popstate", handlePopState); activeSheetCount = Math.max(0, activeSheetCount - 1); if (activeSheetCount === 0) { + if ( + ownsHistoryEntry && + window.history.state?.[SHEET_HISTORY_KEY] === true + ) { + window.history.back(); + } document.body.style.overflow = initialBodyOverflow; document.documentElement.style.overflow = initialHtmlOverflow; if (lockedAppShell) {