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");
+}