Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.

Commit 0f87c88

Browse files
authored
Merge pull request bootstrap-vue-next#1458 from xvaara/dropdown-container
feat(BDropdown): Add container prop to dropdown
2 parents b79790a + 0e9866d commit 0f87c88

File tree

4 files changed

+32
-13
lines changed

4 files changed

+32
-13
lines changed

apps/docs/src/docs/components/dropdown.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,10 @@ By default, dropdowns are visually constrained to their clipping ancestors, whic
330330

331331
**Note:** When `boundary` is any value other than the default of `clippingAncestors`, the style `position: static` is applied to the dropdown component's root element to allow the menu to "break out" of its scroll container. In some situations, this may affect your layout or positioning of the dropdown trigger button. In these cases, you may need to wrap your dropdown inside another element
332332

333+
### Container element
334+
335+
By default, dropdowns are next to the toggle button. However, you can specify a container element via the `container` prop where dropdowns will be teleported to instead. Supported values are CSS selector string, an actual DOM node or a reference to an HTML element.
336+
333337
### Dropdown auto close behavior
334338

335339
By default, the dropdown menu is closed when clicking inside or outside the dropdown menu. You can use the `auto-close` property to change this behavior of the dropdown.

packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,21 @@
3737
</slot>
3838
</span>
3939
</BButton>
40-
<ul
41-
v-if="!lazyBoolean || modelValueBoolean"
42-
v-show="lazyBoolean || modelValueBoolean"
43-
ref="floating"
44-
:style="floatingStyles"
45-
class="dropdown-menu show"
46-
:class="dropdownMenuClasses"
47-
:aria-labelledby="computedId"
48-
:role="role"
49-
@click="onClickInside"
50-
>
51-
<slot :hide="close" :show="open" />
52-
</ul>
40+
<RenderComponentOrSkip :tag="'Teleport'" :to="container" :skip="!container">
41+
<ul
42+
v-if="!lazyBoolean || modelValueBoolean"
43+
v-show="lazyBoolean || modelValueBoolean"
44+
ref="floating"
45+
:style="floatingStyles"
46+
class="dropdown-menu show"
47+
:class="dropdownMenuClasses"
48+
:aria-labelledby="computedId"
49+
:role="role"
50+
@click="onClickInside"
51+
>
52+
<slot :hide="close" :show="open" />
53+
</ul>
54+
</RenderComponentOrSkip>
5355
</div>
5456
</template>
5557

@@ -70,6 +72,8 @@ import {useBooleanish, useId} from '../../composables'
7072
import type {BDropdownProps} from '../../types'
7173
import {BvTriggerableEvent, dropdownInjectionKey, resolveFloatingPlacement} from '../../utils'
7274
import BButton from '../BButton/BButton.vue'
75+
import RenderComponentOrSkip from '../RenderComponentOrSkip.vue'
76+
7377
// TODO add navigation through keyboard events
7478
// TODO standardize keydown vs keyup events globally
7579
@@ -108,6 +112,7 @@ const props = withDefaults(defineProps<BDropdownProps>(), {
108112
strategy: 'absolute',
109113
splitTo: undefined,
110114
boundary: 'clippingAncestors',
115+
container: undefined,
111116
})
112117
113118
const emit = defineEmits<{

packages/bootstrap-vue-next/src/components/BDropdown/dropdown.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,4 +466,12 @@ describe('dropdown', () => {
466466
await $bbutton.trigger('click')
467467
expect(wrapper.emitted()).toHaveProperty('toggle')
468468
})
469+
it('container prop to teleport to body', async () => {
470+
const wrapper = mount(BDropdown, {
471+
props: {container: 'body'},
472+
})
473+
expect(wrapper.exists()).toBe(true)
474+
expect(wrapper.element.querySelector('ul')).toBe(null)
475+
expect(document.body?.querySelector('.dropdown-menu')).not.toBe(null)
476+
})
469477
})

packages/bootstrap-vue-next/src/types/ComponentProps.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {Size} from './Size'
1212
import type {ButtonVariant} from './ButtonVariant'
1313
import type {ButtonType} from './ButtonType'
1414
import type {Boundary, Middleware, RootBoundary, Strategy} from '@floating-ui/vue'
15+
import type {ComponentPublicInstance} from 'vue'
1516

1617
export interface BLinkProps {
1718
active?: Booleanish
@@ -161,6 +162,7 @@ export interface BDropdownProps {
161162
floatingMiddleware?: Middleware[]
162163
splitTo?: RouteLocationRaw
163164
boundary?: Boundary | RootBoundary
165+
container?: string | ComponentPublicInstance<HTMLElement> | HTMLElement | undefined
164166
}
165167

166168
interface BToastIntermediate {

0 commit comments

Comments
 (0)