fix: CSAT select label dropdown overflow issue (#11499)

# Pull Request Template

## Description

This PR fixes a UI bug where the "Select Label" dropdown in the CSAT
survey rule overflowed off the screen. The dropdown now stays within the
visible bounds.


## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Loom video

https://www.loom.com/share/6a26ba5524de4fcdb2f209a6ec215ba6?sid=7cf1c84c-b167-4237-9571-00af56f7da18


## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
This commit is contained in:
Sivin Varghese
2025-05-17 04:41:42 +05:30
committed by GitHub
parent eba24ce275
commit 6dc73841f2

View File

@@ -1,5 +1,6 @@
<script setup> <script setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import { useElementBounding, useWindowSize } from '@vueuse/core';
import DropdownContainer from 'next/dropdown-menu/base/DropdownContainer.vue'; import DropdownContainer from 'next/dropdown-menu/base/DropdownContainer.vue';
import DropdownSection from 'next/dropdown-menu/base/DropdownSection.vue'; import DropdownSection from 'next/dropdown-menu/base/DropdownSection.vue';
import DropdownBody from 'next/dropdown-menu/base/DropdownBody.vue'; import DropdownBody from 'next/dropdown-menu/base/DropdownBody.vue';
@@ -36,6 +37,13 @@ const selected = defineModel({
required: true, required: true,
}); });
const triggerRef = ref(null);
const dropdownRef = ref(null);
const { top } = useElementBounding(triggerRef);
const { height } = useWindowSize();
const { height: dropdownHeight } = useElementBounding(dropdownRef);
const selectedOption = computed(() => { const selectedOption = computed(() => {
return props.options.find(o => o.value === selected.value) || {}; return props.options.find(o => o.value === selected.value) || {};
}); });
@@ -45,6 +53,16 @@ const iconToRender = computed(() => {
return selectedOption.value.icon || 'i-lucide-chevron-down'; return selectedOption.value.icon || 'i-lucide-chevron-down';
}); });
const dropdownPosition = computed(() => {
const DROPDOWN_MAX_HEIGHT = 340;
// Get actual height if available or use default
const menuHeight = dropdownHeight.value
? dropdownHeight.value + 20
: DROPDOWN_MAX_HEIGHT;
const spaceBelow = height.value - top.value;
return spaceBelow < menuHeight ? 'bottom-0' : 'top-0';
});
const updateSelected = newValue => { const updateSelected = newValue => {
selected.value = newValue; selected.value = newValue;
}; };
@@ -55,6 +73,7 @@ const updateSelected = newValue => {
<template #trigger="{ toggle }"> <template #trigger="{ toggle }">
<slot name="trigger" :toggle="toggle"> <slot name="trigger" :toggle="toggle">
<Button <Button
ref="triggerRef"
sm sm
slate slate
:variant :variant
@@ -65,7 +84,12 @@ const updateSelected = newValue => {
/> />
</slot> </slot>
</template> </template>
<DropdownBody class="top-0 min-w-48 z-50" strong> <DropdownBody
ref="dropdownRef"
class="min-w-48 z-50"
:class="dropdownPosition"
strong
>
<DropdownSection class="max-h-80 overflow-scroll"> <DropdownSection class="max-h-80 overflow-scroll">
<DropdownItem <DropdownItem
v-for="option in options" v-for="option in options"