diff --git a/src/components/Componentes/question-sheet.test.tsx b/src/components/Componentes/question-sheet.test.tsx index 0a3449d..205dd26 100644 --- a/src/components/Componentes/question-sheet.test.tsx +++ b/src/components/Componentes/question-sheet.test.tsx @@ -214,14 +214,14 @@ describe("QuestionSheet component", () => { pushState.mockRestore(); }); - it("opens a searchable sheet without focusing search", () => { + it("opens a searchable sheet for 7+ options without focusing search", () => { const question = { id: "q_country", title: "کشور", type: "dropdown", required: true, extras: { placeHolder: "انتخاب کشور" }, - options: Array.from({ length: 6 }, (_, index) => ({ + options: Array.from({ length: 7 }, (_, index) => ({ id: `country-${index}`, value: `country-${index}`, label: `کشور ${index + 1}`, @@ -246,7 +246,7 @@ describe("QuestionSheet component", () => { ); }); - it("sizes a short options sheet to its content", () => { + it("sizes a short options sheet to its content (4 options)", () => { const question = { id: "q_short", title: "انتخاب کوتاه", @@ -277,4 +277,99 @@ describe("QuestionSheet component", () => { "max-h-[82svh]", ); }); + + it("compact boundary: 5 options → compact, no search", () => { + const question = { + id: "q_boundary_5", + title: "پنج گزینه", + type: "dropdown", + required: true, + extras: { placeHolder: "انتخاب" }, + options: Array.from({ length: 5 }, (_, i) => ({ + id: `opt-${i}`, + value: `opt-${i}`, + label: `گزینه ${i + 1}`, + order: i + 1, + })), + ui_config: {}, + } as QuestionField; + + render( + + + + + , + ); + + fireEvent.click(screen.getByRole("button", { name: "انتخاب" })); + + const section = screen.getByRole("dialog").querySelector("section"); + expect(section).toHaveClass("h-auto", "max-h-[82svh]"); + expect(screen.queryByPlaceholderText("جستجو...")).toBeNull(); + }); + + it("compact boundary: 6 options → compact, no search", () => { + const question = { + id: "q_boundary_6", + title: "شش گزینه", + type: "dropdown", + required: true, + extras: { placeHolder: "انتخاب" }, + options: Array.from({ length: 6 }, (_, i) => ({ + id: `opt-${i}`, + value: `opt-${i}`, + label: `گزینه ${i + 1}`, + order: i + 1, + })), + ui_config: {}, + } as QuestionField; + + render( + + + + + , + ); + + fireEvent.click(screen.getByRole("button", { name: "انتخاب" })); + + const section = screen.getByRole("dialog").querySelector("section"); + expect(section).toHaveClass("h-auto", "max-h-[82svh]"); + expect(section).not.toHaveClass("h-[82svh]"); + expect(screen.queryByPlaceholderText("جستجو...")).toBeNull(); + }); + + it("compact boundary: 7 options → large sheet with search", () => { + const question = { + id: "q_boundary_7", + title: "هفت گزینه", + type: "dropdown", + required: true, + extras: { placeHolder: "انتخاب" }, + options: Array.from({ length: 7 }, (_, i) => ({ + id: `opt-${i}`, + value: `opt-${i}`, + label: `گزینه ${i + 1}`, + order: i + 1, + })), + ui_config: {}, + } as QuestionField; + + render( + + + + + , + ); + + fireEvent.click(screen.getByRole("button", { name: "انتخاب" })); + + const section = screen.getByRole("dialog").querySelector("section"); + expect(section).toHaveClass("h-[82svh]"); + expect(section).not.toHaveClass("h-auto"); + expect(screen.getByPlaceholderText("جستجو...")).toBeDefined(); + }); }); diff --git a/src/components/Componentes/question-sheet.tsx b/src/components/Componentes/question-sheet.tsx index c37fc47..d2829cb 100644 --- a/src/components/Componentes/question-sheet.tsx +++ b/src/components/Componentes/question-sheet.tsx @@ -73,11 +73,11 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { const options = question.options || []; - const showSearch = (() => { - if (question.extras?.noSearch) return false; - return options.length > 5; - })(); - const isCompact = options.length <= 5; + const COMPACT_OPTIONS_MAX = 6; + + const isCompact = options.length <= COMPACT_OPTIONS_MAX; + const showSearch = + !question.extras?.noSearch && options.length > COMPACT_OPTIONS_MAX; useEffect(() => { if (!isOpen || isClosing || !isCompact) return; diff --git a/src/lib/webview-actions.ts b/src/lib/webview-actions.ts index 90e82ee..4b26031 100644 --- a/src/lib/webview-actions.ts +++ b/src/lib/webview-actions.ts @@ -2,7 +2,7 @@ * WebView Utility Actions * * Helper module for sending utility actions to Flutter via HabibApp channel. - * Actions: download_file, copy_to_clipboard, open_external_url, upload_file + * Actions: download_file, copy_to_clipboard, open_external_url, upload_file, buy_coin * * Docs: WEBVIEW_ACTION_DOWNLOAD.md */ @@ -150,3 +150,19 @@ export function uploadFile(options: UploadFileOptions): boolean { options as unknown as Record, ); } + +// ─── buy_coin ──────────────────────────────────────────── + +/** + * Ask Flutter to open the Habib Coin packages list page (Add Coin). + * + * Used when a payment fails with "Not enough coins": the user tops up + * natively, returns to the WebView and retries the payment. + * + * ```ts + * buyHabibCoinPackages(); + * ``` + */ +export function buyHabibCoinPackages(): boolean { + return postActionToFlutter("buy_coin"); +}