aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/src/dropdown.js11
-rw-r--r--js/tests/unit/dropdown.spec.js52
-rw-r--r--site/content/docs/5.0/components/dropdowns.md4
3 files changed, 64 insertions, 3 deletions
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 4d65008f8..008294e9b 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -84,7 +84,7 @@ const DefaultType = {
offset: '(number|string|function)',
flip: 'boolean',
boundary: '(string|element)',
- reference: '(string|element)',
+ reference: '(string|element|object)',
display: 'string',
popperConfig: '(null|object)'
}
@@ -172,6 +172,8 @@ class Dropdown extends BaseComponent {
if (typeof this._config.reference.jquery !== 'undefined') {
referenceElement = this._config.reference[0]
}
+ } else if (typeof this._config.reference === 'object') {
+ referenceElement = this._config.reference
}
this._popper = Popper.createPopper(referenceElement, this._menu, this._getPopperConfig())
@@ -257,6 +259,13 @@ class Dropdown extends BaseComponent {
typeCheckConfig(NAME, config, this.constructor.DefaultType)
+ if (typeof config.reference === 'object' && !isElement(config.reference) &&
+ typeof config.reference.getBoundingClientRect !== 'function'
+ ) {
+ // Popper virtual elements require a getBoundingClientRect method
+ throw new Error(`${NAME}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`)
+ }
+
return config
}
diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js
index d2171f369..ba1d0f443 100644
--- a/js/tests/unit/dropdown.spec.js
+++ b/js/tests/unit/dropdown.spec.js
@@ -367,6 +367,58 @@ describe('Dropdown', () => {
dropdown.toggle()
})
+ it('should toggle a dropdown with a valid virtual element reference', done => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle visually-hidden" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const virtualElement = {
+ getBoundingClientRect() {
+ return {
+ width: 0,
+ height: 0,
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0
+ }
+ }
+ }
+
+ expect(() => new Dropdown(btnDropdown, {
+ reference: {}
+ })).toThrow()
+
+ expect(() => new Dropdown(btnDropdown, {
+ reference: {
+ getBoundingClientRect: 'not-a-function'
+ }
+ })).toThrow()
+
+ // use onFirstUpdate as Poppers internal update is executed async
+ const dropdown = new Dropdown(btnDropdown, {
+ reference: virtualElement,
+ popperConfig: {
+ onFirstUpdate() {
+ expect(virtualElement.getBoundingClientRect).toHaveBeenCalled()
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ done()
+ }
+ }
+ })
+
+ spyOn(virtualElement, 'getBoundingClientRect').and.callThrough()
+
+ dropdown.toggle()
+ })
+
it('should not toggle a dropdown if the element is disabled', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
diff --git a/site/content/docs/5.0/components/dropdowns.md b/site/content/docs/5.0/components/dropdowns.md
index 50dcde2ba..a50dcf0f5 100644
--- a/site/content/docs/5.0/components/dropdowns.md
+++ b/site/content/docs/5.0/components/dropdowns.md
@@ -886,9 +886,9 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
</tr>
<tr>
<td><code>reference</code></td>
- <td>string | element</td>
+ <td>string | element | object</td>
<td><code>'toggle'</code></td>
- <td>Reference element of the dropdown menu. Accepts the values of <code>'toggle'</code>, <code>'parent'</code>, or an HTMLElement reference. For more information refer to Popper's <a href="https://popper.js.org/docs/v2/constructors/#createpopper">constructor docs</a>.</td>
+ <td>Reference element of the dropdown menu. Accepts the values of <code>'toggle'</code>, <code>'parent'</code>, an HTMLElement reference or an object providing <code>getBoundingClientRect</code>. For more information refer to Popper's <a href="https://popper.js.org/docs/v2/constructors/#createpopper">constructor docs</a> and <a href="https://popper.js.org/docs/v2/virtual-elements/">virtual element docs</a>.</td>
</tr>
<tr>
<td><code>display</code></td>