diff --git a/src/components/BaseMultiselect.vue b/src/components/BaseMultiselect.vue index 6796cce..865f321 100644 --- a/src/components/BaseMultiselect.vue +++ b/src/components/BaseMultiselect.vue @@ -1,14 +1,93 @@ + + - - diff --git a/src/components/SingleSelect.vue b/src/components/SingleSelect.vue index f6cdede..0c7c4eb 100644 --- a/src/components/SingleSelect.vue +++ b/src/components/SingleSelect.vue @@ -1,69 +1,98 @@ diff --git a/src/composables/useDropdown.js b/src/composables/useDropdown.js new file mode 100644 index 0000000..20f1e97 --- /dev/null +++ b/src/composables/useDropdown.js @@ -0,0 +1,13 @@ +import { ref } from 'vue' + +export function useDropdown() { + const isOpen = ref(false) + + const toggleDropdown = () => { + isOpen.value = !isOpen.value + } + const closeDropdown = () => { + isOpen.value = false + } + return { isOpen, toggleDropdown, closeDropdown } +} diff --git a/src/composables/click-outside.js b/src/directives/click-outside.js similarity index 100% rename from src/composables/click-outside.js rename to src/directives/click-outside.js diff --git a/src/main.js b/src/main.js index 98ce12f..c3dd019 100644 --- a/src/main.js +++ b/src/main.js @@ -1,11 +1,11 @@ // directives -import clickOutside from "./composables/click-outside.js"; +import clickOutside from './directives/click-outside.js' -import { createApp } from "vue"; -import App from "./App.vue"; +import { createApp } from 'vue' +import App from './App.vue' -const app = createApp(App); +const app = createApp(App) -app.directive("click-outside", clickOutside); +app.directive('click-outside', clickOutside) -app.mount("#app"); +app.mount('#app') diff --git a/tests/base-multiselect.test.js b/tests/base-multiselect.test.js new file mode 100644 index 0000000..008bd59 --- /dev/null +++ b/tests/base-multiselect.test.js @@ -0,0 +1,41 @@ +import { describe, it, expect } from "vitest"; +import { mount } from "@vue/test-utils"; +import BaseMultiselect from "../src/components/BaseMultiselect.vue"; + +const clickOutsideStub = { + beforeMount() {}, + unmounted() {} +}; + +describe("BaseMultiselect wrapper behavior", () => { + it("renders placeholder when modelValue is empty", () => { + const wrapper = mount(BaseMultiselect, { + props: { options: ["A", "B"], modelValue: null, placeholder: "Select..." }, + global: { directives: { "click-outside": clickOutsideStub } } + }); + expect(wrapper.find(".selected-display").text()).toContain("Select..."); + }); + + it("toggles dropdown visibility on click", async () => { + const wrapper = mount(BaseMultiselect, { + props: { options: ["A", "B"], modelValue: null }, + global: { directives: { "click-outside": clickOutsideStub } } + }); + expect(wrapper.find(".options-dropdown").exists()).toBe(false); + await wrapper.find(".selected-display").trigger("click"); + expect(wrapper.find(".options-dropdown").exists()).toBe(true); + }); + + it("emits search on input", async () => { + const wrapper = mount(BaseMultiselect, { + props: { options: ["A", "B"], modelValue: null }, + global: { directives: { "click-outside": clickOutsideStub } } + }); + const input = wrapper.find(".search-input"); + await input.setValue("B"); + await input.trigger("input"); + const events = wrapper.emitted("search"); + expect(events).toBeTruthy(); + expect(events[0][0]).toBe("B"); + }); +}); \ No newline at end of file diff --git a/tests/example.test.js b/tests/example.test.js deleted file mode 100644 index f1e49df..0000000 --- a/tests/example.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { mount } from "@vue/test-utils"; -import BaseMultiselect from "../src/components/BaseMultiselect.vue"; - -describe("BaseMultiselect Component", () => { - it("should render without errors", () => { - const wrapper = mount(BaseMultiselect); - expect(wrapper.exists()).toBe(true); - expect(wrapper.text()).toContain("Base Multiselect Component"); - }); -}); diff --git a/tests/single-select-variant.test.js b/tests/single-select-variant.test.js index 40da67b..16e8c62 100644 --- a/tests/single-select-variant.test.js +++ b/tests/single-select-variant.test.js @@ -2,12 +2,19 @@ import { describe, it, expect } from "vitest"; import { mount } from "@vue/test-utils"; import Variant from "../src/components/variants/SingleSelectSimple.vue"; +const clickOutsideStub = { + beforeMount() {}, + unmounted() {} +}; + describe("SingleSelect simple variant", () => { - it("renders two select elements with initial values", () => { - const wrapper = mount(Variant); - const selects = wrapper.findAll("select"); - expect(selects.length).toBe(2); - expect(selects[0].element.value).toBe("Apple"); - expect(selects[1].element.value).toBe("1"); + it("renders two SingleSelects with initial labels", () => { + const wrapper = mount(Variant, { + global: { directives: { "click-outside": clickOutsideStub } } + }); + const displays = wrapper.findAll(".selected-display"); + expect(displays.length).toBe(2); + expect(displays[0].text()).toContain("Apple"); + expect(displays[1].text()).toContain("One"); }); }); diff --git a/tests/single-select.test.js b/tests/single-select.test.js index 1a5d82d..05de8ed 100644 --- a/tests/single-select.test.js +++ b/tests/single-select.test.js @@ -2,13 +2,22 @@ import { describe, it, expect } from "vitest"; import { mount } from "@vue/test-utils"; import SingleSelect from "../src/components/SingleSelect.vue"; +const clickOutsideStub = { + beforeMount() {}, + unmounted() {} +}; + describe("SingleSelect Component", () => { it("emits selected primitive value", async () => { const wrapper = mount(SingleSelect, { - props: { options: ["a", "b", "c"] } + props: { options: ["a", "b", "c"], modelValue: "" }, + global: { directives: { "click-outside": clickOutsideStub } } }); - await wrapper.find("select").setValue("b"); + await wrapper.find(".selected-display").trigger("click"); + const options = wrapper.findAll(".single-select-options li"); + expect(options.length).toBe(3); + await options[1].trigger("click"); expect(wrapper.emitted()["update:modelValue"][0]).toEqual(["b"]); }); @@ -18,9 +27,15 @@ describe("SingleSelect Component", () => { { label: "One", value: 1 }, { label: "Two", value: 2 } ]; - const wrapper = mount(SingleSelect, { props: { options } }); + const wrapper = mount(SingleSelect, { + props: { options, modelValue: "" }, + global: { directives: { "click-outside": clickOutsideStub } } + }); - await wrapper.find("select").setValue("2"); + await wrapper.find(".selected-display").trigger("click"); + const items = wrapper.findAll(".single-select-options li"); + expect(items.length).toBe(2); + await items[1].trigger("click"); expect(wrapper.emitted()["update:modelValue"][0]).toEqual([options[1]]); });