Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: The popover is not displaying properly when it is placed inside the form. #931

Open
aakashearth9 opened this issue Dec 2, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@aakashearth9
Copy link

Reproduction

https://stackblitz.com/edit/tdkd3r-uvsouz

Describe the bug

The dropdown is implemented using a Popover component, which displays the options. However, when an option is selected, the Popover unexpectedly repositions itself to the top of the page, rather than staying below the input field.

Is there a way to force the Popover component to stay positioned below the input field, even when an option is selected?

image

<template>
  <form class="w-full space-y-6" @submit.prevent="onSubmit">
    <FormField v-slot="{ componentField }" name="roles">
      <FormItem>
        <FormLabel>Role</FormLabel>
        <FormControl>
          <MultiSelect
            v-bind="componentField"
            :options="frameworksList"
            :onValueChange="componentField.onChange"
            :defaultValue="componentField.modelValue"
            placeholder="Select options"
            variant="inverted"
            :animation="2"
            :maxCount="10"
          />
        </FormControl>
        <FormMessage />
      </FormItem>
    </FormField>
  </form>
</template>

##Multi Select##

<Popover v-model:open="isPopoverOpen" :onOpenToggle="togglePopover">
    <PopoverTrigger asChild>
      <template v-if="selectedValues.length > 0">
        <TagsInput :model-value="defaultValue"
          class="flex w-full p-1 rounded-md border min-h-10 h-auto bg-inherit hover:bg-inherit">
          <TagsInputItem v-for="item in defaultValue.slice(0, maxCount)" :key="item" :value="item">
            <Badge :key="item" :class="cn(multiSelectVariants({ variant }))"
              :style="{ animationDuration: `${animation}s` }">
              {{ getOption(item)?.label || item }}
              <XCircle class="ml-2 h-4 w-4 cursor-pointer" @click.stop="toggleOption(item)" />
            </Badge>

          </TagsInputItem>
          <!-- Place the controls always at the end -->
          <div class="flex items-center justify-between ml-auto">
            <XIcon class="h-4 mx-2 cursor-pointer text-muted-foreground" @click.stop="clearSelection" />
            <Separator orientation="vertical" class="flex min-h-6 h-full" />
            <ChevronDown class="h-4 mx-2 cursor-pointer text-muted-foreground" />
          </div>
        </TagsInput>
      </template>

      <template v-else>
        <TagsInput :model-value="defaultValue"
          class="flex w-full p-1 rounded-md border min-h-10 h-auto bg-inherit hover:bg-inherit">
          <TagsInputItem :value="{}">

            <div className="flex items-center justify-between w-full mx-auto">
              <span className="text-sm text-muted-foreground mx-3">
                {{ placeholder }}
              </span>
              <ChevronDown className="h-4 cursor-pointer text-muted-foreground mx-2" />
            </div>
          </TagsInputItem>

        </TagsInput>

      </template>

    </PopoverTrigger>
    <PopoverContent class="w-auto p-0" align="start" side="bottom" :sideOffset="4"  :closeOnContentClick="false">
      <Command>
        <CommandInput class="h-9" placeholder="Search framework..." />
        <CommandEmpty>No framework found.</CommandEmpty>
        <CommandList>
          <CommandGroup>
            <CommandItem v-for="framework in options" :key="framework.value" :value="framework.value"
            @select="toggleOption(framework.value); isPopoverOpen = false"
            >
              {{ framework.label }}
              <CheckIcon v-if="selectedValues.includes(framework.value)" class="ml-auto h-4 w-4" />

            </CommandItem>
          </CommandGroup>
        </CommandList>
      </Command>
    </PopoverContent>
</Popover>

System Info

System:
    OS: Windows 11 10.0.26100
    CPU: (16) x64 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
    Memory: 2.76 GB / 15.65 GB
  Binaries:
    Node: 20.14.0 - C:\Program Files\nodejs\node.EXE
    npm: 10.8.3 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (130.0.2849.80)
    Internet Explorer: 11.0.26100.1882
  npmPackages:
    @vueuse/core: ^11.3.0 => 11.3.0
    radix-vue: ^1.9.9 => 1.9.9
    vue: ^3.5.12 => 3.5.12


### Contributes

- [ ] I am willing to submit a PR to fix this issue
- [ ] I am willing to submit a PR with failing tests
@aakashearth9 aakashearth9 added the bug Something isn't working label Dec 2, 2024
@Saeid-Za
Copy link
Contributor

Hello There !
Since you are using as-child prop of PopoverTrigger, the child root must remain unchanged for popperjs to work properly.
In the example provided, you are destroying the root component and then rendering another root component.

<template v-if="selectedValues.length > 0">
...
</template>

<template v-else>
...
</template>

which makes the original DOM element not available during the second opening of Command component.
either 1- wrap the template section with another element or 2- reuse TagsInput without re-rendering.

An example of solution number 1:

<template>
  <Popover v-model:open="isPopoverOpen" :onOpenToggle="togglePopover">
    <PopoverTrigger asChild>
      <div>
        <TagsInput v-if="selectedValues.length > 0"
          :model-value="defaultValue"
          class="flex w-full p-1 rounded-md border min-h-10 h-auto bg-inherit hover:bg-inherit"
        >
          <TagsInputItem
            v-for="item in defaultValue.slice(0, maxCount)"
            :key="item"
            :value="item"
          >
            <Badge
              :key="item"
              :class="cn(multiSelectVariants({ variant }))"
              :style="{ animationDuration: `${animation}s` }"
            >
              {{ getOption(item)?.label || item }}
              <XCircle
                class="ml-2 h-4 w-4 cursor-pointer"
                @click.stop="toggleOption(item)"
              />
            </Badge>
          </TagsInputItem>
          <!-- Place the controls always at the end -->
          <div class="flex items-center justify-between ml-auto">
            <XIcon
              class="h-4 mx-2 cursor-pointer text-muted-foreground"
              @click.stop="clearSelection"
            />
            <Separator orientation="vertical" class="flex min-h-6 h-full" />
            <ChevronDown
              class="h-4 mx-2 cursor-pointer text-muted-foreground"
            />
          </div>
        </TagsInput>

        <TagsInput v-else
          variant="inverted"
          :model-value="defaultValue"
          class="flex w-full p-1 rounded-md border min-h-10 h-auto"
        >
          <TagsInputItem :value="{}">
            <div className="flex items-center justify-between w-full mx-auto">
              <span className="text-sm text-muted-foreground mx-3">
                {{ placeholder }}
              </span>
              <ChevronDown
                className="h-4 cursor-pointer text-muted-foreground mx-2"
              />
            </div>
          </TagsInputItem>
        </TagsInput>
      </div>

    </PopoverTrigger>
    <PopoverContent
      class="w-auto p-0"
      align="start"
      side="bottom"
      :sideOffset="4"
      :closeOnContentClick="false"
    >
      <Command>
        <CommandInput class="h-9" placeholder="Search framework..." />
        <CommandEmpty>No framework found.</CommandEmpty>
        <CommandList>
          <CommandGroup>
            <CommandItem
              v-for="framework in options"
              :key="framework.value"
              :value="framework.value"
              @select="
                toggleOption(framework.value);
                isPopoverOpen = false;
              "
            >
              {{ framework.label }}
              <CheckIcon
                v-if="selectedValues.includes(framework.value)"
                class="ml-auto h-4 w-4"
              />
            </CommandItem>
          </CommandGroup>
        </CommandList>
      </Command>
    </PopoverContent>
  </Popover>
</template>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants