From f9a01cef31504dd2c251833cabda504c752e95e4 Mon Sep 17 00:00:00 2001 From: mortezaei Date: Tue, 18 Aug 2026 12:37:24 +0330 Subject: [PATCH] feat: redesign country selection sheet with enhanced search, list items, and testing support --- ...ss_02d1b36a-d03c-400c-8d4c-a423d16e661f.md | 2 +- .../Componentes/question-phone.test.tsx | 82 ++++++++++ src/components/Componentes/question-phone.tsx | 140 ++++++++++++------ 3 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 src/components/Componentes/question-phone.test.tsx diff --git a/.zcode/plans/plan-sess_02d1b36a-d03c-400c-8d4c-a423d16e661f.md b/.zcode/plans/plan-sess_02d1b36a-d03c-400c-8d4c-a423d16e661f.md index d34e5f4..ae63a2b 100644 --- a/.zcode/plans/plan-sess_02d1b36a-d03c-400c-8d4c-a423d16e661f.md +++ b/.zcode/plans/plan-sess_02d1b36a-d03c-400c-8d4c-a423d16e661f.md @@ -84,4 +84,4 @@ body.section-overlay-open .app-shell { overflow-y: hidden; } - `/en/questions-list` → کلیک سکشن: اسلاید از راست؛ `/fa/questions-list` → اسلاید از چپ. - بستن با دکمه close (flush)، back مرورگر، و شبیه‌سازی هاردور بک — همه با انیمیشن خروج. - refresh مستقیم روی `/en/questions-list/personal_identity` → صفحه کامل. - - حفظ اسکرول لیست پس از بستن؛ قفل اسکرول پشت پنل هنگام باز بودن. \ No newline at end of file + - حفظ اسکرول لیست پس از بستن؛ قفل اسکرول پشت پنل هنگام باز بودن.له \ No newline at end of file diff --git a/src/components/Componentes/question-phone.test.tsx b/src/components/Componentes/question-phone.test.tsx new file mode 100644 index 0000000..232770a --- /dev/null +++ b/src/components/Componentes/question-phone.test.tsx @@ -0,0 +1,82 @@ +import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import type { QuestionField } from "@/lib/schema-adapter"; +import { QuestionPhone } from "./question-phone"; + +afterEach(() => { + cleanup(); +}); + +vi.mock("@/translations/provider", () => ({ + useI18n: () => ({ + dictionary: { + "Select country": "انتخاب کشور", + Close: "بستن", + Confirm: "تایید", + }, + locale: "fa", + }), +})); + +vi.mock("./question-answer-storage", () => ({ + useQuestionAnswers: () => ({ + getAnswerValue: () => null, + setAnswerValue: vi.fn(), + isLoading: false, + }), +})); + +describe("QuestionPhone Component", () => { + const mockQuestion: QuestionField = { + id: "personal_contact_number", + title: "شماره تماس شخصی", + type: "phone", + extras: { + placeHolder: "+98 9123456789", + }, + }; + + it("renders phone input and opens sheet on country code click", async () => { + render(); + + const countryButton = screen.getByRole("button", { name: /\+98/i }); + expect(countryButton).toBeInTheDocument(); + + fireEvent.click(countryButton); + + const dialog = screen.getByRole("dialog"); + expect(dialog).toBeInTheDocument(); + expect(screen.getByText("انتخاب کشور")).toBeInTheDocument(); + + // Close button exists in header + const closeBtn = screen.getByLabelText("بستن"); + expect(closeBtn).toBeInTheDocument(); + + // Search input is present with translated placeholder + const searchInput = screen.getByPlaceholderText("جستجو..."); + expect(searchInput).toBeInTheDocument(); + }); + + it("filters countries when searching and selects a country", async () => { + const { container } = render( + , + ); + + const triggerButton = container.querySelector("button")!; + fireEvent.click(triggerButton); + + const searchInput = screen.getByPlaceholderText("جستجو..."); + fireEvent.change(searchInput, { target: { value: "ایران" } }); + + // Option should be rendered + const iranOption = screen.getByRole("button", { name: /ایران/i }); + expect(iranOption).toBeInTheDocument(); + + fireEvent.click(iranOption); + + // After selection, dialog closes + await waitFor(() => { + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/Componentes/question-phone.tsx b/src/components/Componentes/question-phone.tsx index b2a7bd8..7d41d4c 100644 --- a/src/components/Componentes/question-phone.tsx +++ b/src/components/Componentes/question-phone.tsx @@ -541,6 +541,22 @@ export function QuestionPhone({ const selectCountryTitle = t["Select country"] || question.title; + const searchPlaceholder = + locale === "fa" + ? "جستجو..." + : locale === "ar" + ? "بحث..." + : locale === "tr" + ? "Ara..." + : "Search..."; + + const noResultsText = + locale === "fa" + ? "موردی یافت نشد" + : locale === "ar" + ? "لم يتم العثور على نتائج" + : "No options found"; + return (
-
-

+ {/* Header with Title and Close Button */} +
+

{selectCountryTitle} -

-

+ + +
-
-
+ {/* Search Bar */} +
+
setSearchQuery(e.target.value)} - aria-label={selectCountryTitle} - placeholder={ - locale === "fa" - ? "جستجوی کشور یا پیش‌شماره..." - : "Search country or dial code..." - } + placeholder={searchPlaceholder} className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]" /> {searchQuery ? ( @@ -700,63 +733,82 @@ export function QuestionPhone({
+ {/* Country Options List */}
event.stopPropagation()} onTouchMove={(event) => event.stopPropagation()} onTouchEnd={(event) => event.stopPropagation()} - className="flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain px-5" + className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain px-5 py-3" > {filteredCountries.length > 0 ? ( filteredCountries.map((c) => { + const cleanCode = c.code.replace(/[^\d]/g, ""); + const activeCleanCode = (codeValue || "").replace( + /[^\d]/g, + "", + ); + const isSelected = cleanCode === activeCleanCode; + return ( ); }) ) : ( - - {locale === "fa" ? "موردی یافت نشد" : "No options found"} - +
+ {noResultsText} +
)}