From 19f792bedd315237e5e9635d88b97645769e6d07 Mon Sep 17 00:00:00 2001 From: mortezaei Date: Mon, 17 Aug 2026 14:53:32 +0330 Subject: [PATCH] feat: implement stable occlusion lift calculation in viewport coordinator with corresponding unit tests --- .../Componentes/question-snap-list.test.tsx | 32 ++++++++++++++++--- .../question-viewport-coordinator.ts | 17 ++++++---- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/components/Componentes/question-snap-list.test.tsx b/src/components/Componentes/question-snap-list.test.tsx index b9e6f66..78f0bfd 100644 --- a/src/components/Componentes/question-snap-list.test.tsx +++ b/src/components/Componentes/question-snap-list.test.tsx @@ -6,8 +6,11 @@ import { waitFor, } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { updateQuestionKeyboardHeight } from "./question-viewport-coordinator"; import { QuestionSnapList } from "./question-snap-list"; +import { + computeQuestionLift, + updateQuestionKeyboardHeight, +} from "./question-viewport-coordinator"; vi.mock("@/translations/provider", () => ({ useI18n: () => ({ dictionary: {} }), @@ -20,6 +23,25 @@ describe("QuestionSnapList keyboard interaction", () => { document.documentElement.style.removeProperty("--question-lift-y"); }); + it("computes a stable occlusion lift without overshoot", () => { + expect( + computeQuestionLift({ + baseTop: 300, + baseBottom: 600, + visibleTop: 100, + visibleBottom: 500, + }), + ).toBe(150); + expect( + computeQuestionLift({ + baseTop: 300, + baseBottom: 600, + visibleTop: 100, + visibleBottom: 800, + }), + ).toBe(0); + }); + it("does not treat pointer contact as input focus", () => { render( @@ -74,9 +96,7 @@ describe("QuestionSnapList keyboard interaction", () => { expect(firstInput).not.toHaveFocus(); expect(secondInput).not.toHaveFocus(); expect( - document.documentElement.style.getPropertyValue( - "--question-lift-y", - ), + document.documentElement.style.getPropertyValue("--question-lift-y"), ).toBe("0px"); }); }); @@ -147,7 +167,9 @@ describe("QuestionSnapList keyboard interaction", () => { input.focus(); updateQuestionKeyboardHeight(300); - await waitFor(() => expect(document.body).toHaveClass("question-keyboard-open")); + await waitFor(() => + expect(document.body).toHaveClass("question-keyboard-open"), + ); updateQuestionKeyboardHeight(0); await waitFor(() => { diff --git a/src/components/Componentes/question-viewport-coordinator.ts b/src/components/Componentes/question-viewport-coordinator.ts index c006dc6..111d21b 100644 --- a/src/components/Componentes/question-viewport-coordinator.ts +++ b/src/components/Componentes/question-viewport-coordinator.ts @@ -56,7 +56,9 @@ export function computeQuestionLift({ return Math.min(Math.max(centerShift, overlapShift), maxShift); } -function isKeyboardInputTarget(target: EventTarget | null): target is HTMLElement { +function isKeyboardInputTarget( + target: EventTarget | null, +): target is HTMLElement { if (target instanceof HTMLTextAreaElement) { return !target.disabled && !target.readOnly; } @@ -82,10 +84,7 @@ export function isActiveQuestionKeyboardInput( function setLift(nextLift: number) { currentLift = Math.max(0, nextLift); - document.documentElement.style.setProperty( - LIFT_VARIABLE, - `${currentLift}px`, - ); + document.documentElement.style.setProperty(LIFT_VARIABLE, `${currentLift}px`); document.body.classList.toggle( "question-keyboard-open", currentLift > 0 && keyboardVisible && activeQuestionInput !== null, @@ -220,7 +219,8 @@ export function useQuestionViewportCoordinator() { }); const handleViewportResize = () => { - const viewportHeight = window.visualViewport?.height ?? window.innerHeight; + const viewportHeight = + window.visualViewport?.height ?? window.innerHeight; const reduction = closedViewportHeight - viewportHeight; if (reduction > KEYBOARD_THRESHOLD) { @@ -246,7 +246,10 @@ export function useQuestionViewportCoordinator() { return () => { document.removeEventListener("focusin", handleFocusIn); document.removeEventListener("focusout", handleFocusOut); - window.visualViewport?.removeEventListener("resize", handleViewportResize); + window.visualViewport?.removeEventListener( + "resize", + handleViewportResize, + ); unsubscribeConfig(); resetQuestionKeyboardState(); };