Skip to content

Commit afb0b1a

Browse files
committed
fix(menu): ensure root-class is applied to teleport menu
see #322
1 parent 1ddf7e1 commit afb0b1a

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

src/Menu.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script setup lang="ts" generic="GenericOption extends Option<OptionValue>, OptionValue = string">
2+
import type { HTMLAttributes } from "vue";
23
import type { DataInjection, PropsInjection } from "./lib/provide-inject";
34
import type { Option } from "./types/option";
45
import type { MenuSlots } from "./types/slots";
@@ -9,6 +10,7 @@ import MenuOption from "./MenuOption.vue";
910
1011
const props = defineProps<{
1112
slots: MenuSlots<GenericOption, OptionValue>;
13+
rootClass?: HTMLAttributes["class"];
1214
}>();
1315
1416
const selected = defineModel<OptionValue | OptionValue[]>({ required: true });
@@ -133,7 +135,7 @@ onBeforeUnmount(() => {
133135
:id="`vue-select-${sharedProps.uid}-listbox`"
134136
ref="menu"
135137
class="menu"
136-
:class="sharedProps.classes?.menuContainer"
138+
:class="[sharedProps.classes?.menuContainer, props.rootClass]"
137139
role="listbox"
138140
:aria-label="sharedProps.aria?.labelledby"
139141
:aria-multiselectable="sharedProps.isMulti"

src/Select.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,34 @@ describe("menu opening behavior", () => {
595595
});
596596
});
597597

598+
describe("teleport class propagation", () => {
599+
it("adds root custom class to teleported menu", async () => {
600+
const wrapper = mount(VueSelect, {
601+
props: { modelValue: null, options, teleport: "body" },
602+
attrs: { class: "custom-select" },
603+
});
604+
605+
await openMenu(wrapper);
606+
607+
const menus = Array.from(document.body.querySelectorAll(".menu"));
608+
const menuEl = menus[menus.length - 1] as HTMLElement | undefined;
609+
610+
expect(menuEl).toBeTruthy();
611+
if (!menuEl) {
612+
return;
613+
};
614+
615+
// Should have user-defined class copied from root container
616+
expect(menuEl.classList.contains("custom-select")).toBe(true);
617+
618+
// Should not copy internal classes
619+
expect(menuEl.classList.contains("vue-select")).toBe(false);
620+
expect(menuEl.classList.contains("open")).toBe(false);
621+
expect(menuEl.classList.contains("typing")).toBe(false);
622+
expect(menuEl.classList.contains("disabled")).toBe(false);
623+
});
624+
});
625+
598626
describe("menu closing behavior", () => {
599627
it("should close menu with different triggers", async () => {
600628
const wrapper = mount(VueSelect, { props: { modelValue: null, options } });

src/Select.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<script setup lang="ts" generic="GenericOption extends Option<OptionValue>, OptionValue = string">
2+
import type { HTMLAttributes } from "vue";
23
import type { Option } from "./types/option";
34
import type { Props } from "./types/props";
45
import type { Slots } from "./types/slots";
5-
import { computed, provide, ref, useTemplateRef, watch } from "vue";
6+
import { computed, provide, ref, useAttrs, useTemplateRef, watch } from "vue";
67
import Indicators from "./Indicators.vue";
78
import { DATA_KEY, PROPS_KEY } from "./lib/provide-inject";
89
import { uniqueId } from "./lib/uid";
@@ -50,6 +51,9 @@ const emit = defineEmits<{
5051
const slots = defineSlots<Slots<GenericOption, OptionValue>>();
5152
const selected = defineModel<OptionValue | OptionValue[]>({ required: true });
5253
54+
const attrs = useAttrs();
55+
const rootClass = computed(() => attrs.class as HTMLAttributes["class"]);
56+
5357
const containerRef = useTemplateRef("container");
5458
const inputRef = useTemplateRef("input");
5559
const indicatorsRef = useTemplateRef("indicators");
@@ -447,6 +451,7 @@ watch(
447451
<Menu
448452
v-if="menuOpen"
449453
v-model="selected"
454+
:root-class="rootClass"
450455
:slots="{
451456
'option': slots.option,
452457
'menu-header': slots['menu-header'],

0 commit comments

Comments
 (0)