Browse Source

refactor(ui): improve input focus handling and test reliability

Refactor the focus synchronization logic to include pointer events and
ensure test stability when manipulating browser history.

- Add `pointerdown` event listener to `useQuestionInputFocusSync` to
  improve input activation on touch/pointer devices
- Extract input activation logic into a dedicated `activateFocusedQuestionInput`
  function for better readability
- Update `QuestionSheet` tests to reset window history state and use
  asynchronous waiting for `pushState` calls to prevent race conditions
Dev
mortezaei 1 week ago
parent
commit
788c7335de
  1. 4
      src/components/Componentes/question-sheet.test.tsx
  2. 10
      src/components/Componentes/use-sheet-scroll-lock.ts

4
src/components/Componentes/question-sheet.test.tsx

@ -30,6 +30,7 @@ vi.mock("@/hooks/marriage/use-section-data", () => ({
describe("QuestionSheet component", () => { describe("QuestionSheet component", () => {
afterEach(() => { afterEach(() => {
cleanup(); cleanup();
window.history.replaceState(null, "", window.location.href);
document.body.classList.remove("dropdown-open"); document.body.classList.remove("dropdown-open");
document.body.classList.remove("question-sheet-open"); document.body.classList.remove("question-sheet-open");
document.body.classList.remove("question-keyboard-open"); document.body.classList.remove("question-keyboard-open");
@ -202,8 +203,9 @@ describe("QuestionSheet component", () => {
); );
fireEvent.click(screen.getByRole("button", { name: /انتخاب کشور/i })); fireEvent.click(screen.getByRole("button", { name: /انتخاب کشور/i }));
expect(pushState).toHaveBeenCalledTimes(1);
await waitFor(() => expect(pushState).toHaveBeenCalledTimes(1));
window.history.replaceState(null, "", window.location.href);
window.dispatchEvent(new PopStateEvent("popstate")); window.dispatchEvent(new PopStateEvent("popstate"));
await waitFor(() => { await waitFor(() => {

10
src/components/Componentes/use-sheet-scroll-lock.ts

@ -157,7 +157,7 @@ function syncKeyboardShift(isOpen: boolean) {
*/ */
export function useQuestionInputFocusSync() { export function useQuestionInputFocusSync() {
useEffect(() => { useEffect(() => {
const handleFocusIn = (event: FocusEvent) => {
const activateFocusedQuestionInput = (event: Event) => {
if (!isKeyboardInputTarget(event.target)) return; if (!isKeyboardInputTarget(event.target)) return;
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
if (!target.closest('.question-snap-item[aria-current="step"]')) return; if (!target.closest('.question-snap-item[aria-current="step"]')) return;
@ -165,6 +165,12 @@ export function useQuestionInputFocusSync() {
syncQuestionInputOpenClass(); syncQuestionInputOpenClass();
window.requestAnimationFrame(() => syncKeyboardShift(true)); window.requestAnimationFrame(() => syncKeyboardShift(true));
}; };
const handleFocusIn = (event: FocusEvent) => {
activateFocusedQuestionInput(event);
};
const handlePointerDown = (event: PointerEvent) => {
activateFocusedQuestionInput(event);
};
const handleFocusOut = (event: FocusEvent) => { const handleFocusOut = (event: FocusEvent) => {
if (!isKeyboardInputTarget(event.target)) return; if (!isKeyboardInputTarget(event.target)) return;
window.setTimeout(() => { window.setTimeout(() => {
@ -198,10 +204,12 @@ export function useQuestionInputFocusSync() {
document.addEventListener("focusin", handleFocusIn); document.addEventListener("focusin", handleFocusIn);
document.addEventListener("focusout", handleFocusOut); document.addEventListener("focusout", handleFocusOut);
document.addEventListener("pointerdown", handlePointerDown, true);
window.visualViewport?.addEventListener("resize", handleViewportResize); window.visualViewport?.addEventListener("resize", handleViewportResize);
return () => { return () => {
document.removeEventListener("focusin", handleFocusIn); document.removeEventListener("focusin", handleFocusIn);
document.removeEventListener("focusout", handleFocusOut); document.removeEventListener("focusout", handleFocusOut);
document.removeEventListener("pointerdown", handlePointerDown, true);
window.visualViewport?.removeEventListener( window.visualViewport?.removeEventListener(
"resize", "resize",
handleViewportResize, handleViewportResize,

Loading…
Cancel
Save