/* Copyright 2026 Marimo. All rights reserved. */ import { fireEvent, render, screen } from "@testing-library/react"; import { beforeAll, describe, expect, it, vi } from "vitest"; import type { z } from "zod"; import { SetupMocks } from "@/__mocks__/common"; import { initialModeAtom } from "@/core/mode"; import { store } from "@/core/state/jotai"; import type { IPluginProps } from "../../types"; import { DropdownPlugin } from "../DropdownPlugin"; beforeAll(() => { SetupMocks.resizeObserver(); global.HTMLDivElement.prototype.scrollIntoView = () => { // do nothing }; }); describe("DropdownPlugin", () => { beforeAll(() => { store.set(initialModeAtom, "edit"); }); describe("searchable dropdown", () => { it("renders SearchableSelect when searchable is true", () => { const plugin = new DropdownPlugin(); const host = document.createElement("div"); const props: IPluginProps< string[], z.infer<(typeof plugin)["validator"]> > = { data: { label: "Test Label", options: ["Option 1", "Option 2"], allowSelectNone: false, fullWidth: false, searchable: true, initialValue: [], }, value: [], setValue: vi.fn(), host, functions: {}, }; render(plugin.render(props)); expect( screen.getByTestId("marimo-plugin-searchable-dropdown"), ).toBeInTheDocument(); }); it("renders default dropdown when searchable is false", () => { const plugin = new DropdownPlugin(); const host = document.createElement("div"); const props: IPluginProps< string[], z.infer<(typeof plugin)["validator"]> > = { data: { label: "Test Label", options: ["Option 1", "Option 2"], allowSelectNone: false, fullWidth: false, searchable: false, initialValue: [], }, value: [], setValue: vi.fn(), host, functions: {}, }; render(plugin.render(props)); expect(screen.getByTestId("marimo-plugin-dropdown")).toBeInTheDocument(); }); it("filters options based on search input and handles selection", async () => { const plugin = new DropdownPlugin(); const host = document.createElement("div"); const setValue = vi.fn(); const props: IPluginProps< string[], z.infer<(typeof plugin)["validator"]> > = { data: { label: "Test Label", options: ["Apple", "Banana", "Orange"], allowSelectNone: true, fullWidth: false, searchable: true, initialValue: [], }, value: [], setValue, host, functions: {}, }; render(plugin.render(props)); // Open dropdown fireEvent.click( screen.getByTestId("marimo-plugin-searchable-dropdown").firstChild!, ); // Initial empty state const input = screen.getByRole("combobox"); expect(input).toHaveValue(""); // Search functionality fireEvent.change(input, { target: { value: "app" } }); expect(screen.getByText("Apple")).toBeInTheDocument(); expect(screen.queryByText("Banana")).not.toBeInTheDocument(); // Selection after search fireEvent.click(screen.getByText("Apple")); expect(setValue).toHaveBeenCalledWith(["Apple"]); }); it("supports single selection only", async () => { const plugin = new DropdownPlugin(); const host = document.createElement("div"); const setValue = vi.fn(); const props: IPluginProps< string[], z.infer<(typeof plugin)["validator"]> > = { data: { label: "Test Label", options: ["Apple", "Banana", "Orange"], allowSelectNone: true, fullWidth: false, searchable: true, initialValue: [], }, value: ["Apple"], setValue, host, functions: {}, }; const { rerender } = render(plugin.render(props)); // Initial value should be displayed expect( screen.getByTestId("marimo-plugin-searchable-dropdown").firstChild ?.textContent, ).toContain("Apple"); // Open dropdown fireEvent.click( screen.getByTestId("marimo-plugin-searchable-dropdown").firstChild!, ); // Select second option - should replace first fireEvent.click(screen.getByText("Banana")); expect(setValue).toHaveBeenCalledWith(["Banana"]); // Re-render rerender( plugin.render({ ...props, value: ["Banana"], }), ); expect( screen.getByTestId("marimo-plugin-searchable-dropdown").firstChild ?.textContent, ).toContain("Banana"); // Open dropdown fireEvent.click( screen.getByTestId("marimo-plugin-searchable-dropdown").firstChild!, ); // Select none should clear value fireEvent.click(screen.getByText("--")); expect(setValue).toHaveBeenCalledWith([]); }); }); });