diff options
| author | Nils K <[email protected]> | 2020-12-21 13:32:11 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-12-21 14:32:11 +0200 |
| commit | 2d46e47464d15182c44a7d92c8f2476e70f4434e (patch) | |
| tree | 39be89ab0696d4508df55ecc46db23f3e7df0ce1 | |
| parent | 44667d89fa1bf8de15db57fad43696c62860e288 (diff) | |
| download | bootstrap-2d46e47464d15182c44a7d92c8f2476e70f4434e.tar.xz bootstrap-2d46e47464d15182c44a7d92c8f2476e70f4434e.zip | |
Support Popper virtual elements (#32376)
Adds the ability to use objects implementing the virtual element interface as the value for the reference option of a dropdown config.
Co-authored-by: XhmikosR <[email protected]>
| -rw-r--r-- | js/src/dropdown.js | 11 | ||||
| -rw-r--r-- | js/tests/unit/dropdown.spec.js | 52 | ||||
| -rw-r--r-- | site/content/docs/5.0/components/dropdowns.md | 4 |
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> |
