From bd9084d24900325b0559741c8c227cfdab2f22a0 Mon Sep 17 00:00:00 2001
From: 719media <719media@users.noreply.github.com>
Date: Sun, 21 Jan 2018 10:09:23 -0800
Subject: [PATCH] Update dropdown.js to allow referenceElement (#25219)

---
 docs/4.0/components/dropdowns.md | 38 ++++++++++++++++++++++++++++++++
 js/src/dropdown.js               | 25 ++++++++++++++-------
 js/tests/visual/dropdown.html    | 26 ++++++++++++++++++++++
 3 files changed, 81 insertions(+), 8 deletions(-)

diff --git a/docs/4.0/components/dropdowns.md b/docs/4.0/components/dropdowns.md
index 670ea59854..b4c70478ef 100644
--- a/docs/4.0/components/dropdowns.md
+++ b/docs/4.0/components/dropdowns.md
@@ -671,6 +671,38 @@ Add `.disabled` to items in the dropdown to **style them as disabled**.
 </div>
 {% endexample %}
 
+## Dropdown options
+
+Use `data-offset` or `data-reference` to change the location of the dropdown.
+
+{% example html %}
+<div class="d-flex">
+  <div class="dropdown mr-1">
+    <button type="button" class="btn btn-secondary dropdown-toggle" id="dropdownMenuOffset" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-offset="10,20">
+      Offset
+    </button>
+    <div class="dropdown-menu" aria-labelledby="dropdownMenuOffset">
+      <a class="dropdown-item" href="#">Action</a>
+      <a class="dropdown-item" href="#">Another action</a>
+      <a class="dropdown-item" href="#">Something else here</a>
+    </div>
+  </div>
+  <div class="btn-group">
+    <button type="button" class="btn btn-secondary">Reference</button>
+    <button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" id="dropdownMenuReference" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-reference="parent">
+      <span class="sr-only">Toggle Dropdown</span>
+    </button>
+    <div class="dropdown-menu" aria-labelledby="dropdownMenuReference">
+      <a class="dropdown-item" href="#">Action</a>
+      <a class="dropdown-item" href="#">Another action</a>
+      <a class="dropdown-item" href="#">Something else here</a>
+      <div class="dropdown-divider"></div>
+      <a class="dropdown-item" href="#">Separated link</a>
+    </div>
+  </div>
+</div>
+{% endexample %}
+
 ## Usage
 
 Via data attributes or JavaScript, the dropdown plugin toggles hidden content (dropdown menus) by toggling the `.show` class on the parent list item. The `data-toggle="dropdown"` attribute is relied on for closing dropdown menus at an application level, so it's a good idea to always use it.
@@ -740,6 +772,12 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
       <td>'scrollParent'</td>
       <td>Overflow constraint boundary of the dropdown menu. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper.js's <a href="https://popper.js.org/popper-documentation.html#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
     </tr>
+    <tr>
+      <td>reference</td>
+      <td>string | element</td>
+      <td>'toggle'</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.js's <a href="https://popper.js.org/popper-documentation.html#referenceObject">referenceObject docs</a>.</td>
+    </tr>
   </tbody>
 </table>
 
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 2ee37f628a..82deaa2205 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -74,13 +74,15 @@ const Dropdown = (($) => {
   const Default = {
     offset      : 0,
     flip        : true,
-    boundary    : 'scrollParent'
+    boundary    : 'scrollParent',
+    reference   : 'toggle'
   }
 
   const DefaultType = {
     offset      : '(number|string|function)',
     flip        : 'boolean',
-    boundary    : '(string|element)'
+    boundary    : '(string|element)',
+    reference   : '(string|element)'
   }
 
   /**
@@ -150,20 +152,27 @@ const Dropdown = (($) => {
         if (typeof Popper === 'undefined') {
           throw new TypeError('Bootstrap dropdown require Popper.js (https://popper.js.org)')
         }
-        let element = this._element
-        // For dropup with alignment we use the parent as popper container
-        if ($(parent).hasClass(ClassName.DROPUP)) {
-          if ($(this._menu).hasClass(ClassName.MENULEFT) || $(this._menu).hasClass(ClassName.MENURIGHT)) {
-            element = parent
+
+        let referenceElement = this._element
+
+        if (this._config.reference === 'parent') {
+          referenceElement = parent
+        } else if (Util.isElement(this._config.reference)) {
+          referenceElement = this._config.reference
+
+          // Check if it's jQuery element
+          if (typeof this._config.reference.jquery !== 'undefined') {
+            referenceElement = this._config.reference[0]
           }
         }
+
         // If boundary is not `scrollParent`, then set position to `static`
         // to allow the menu to "escape" the scroll parent's boundaries
         // https://github.com/twbs/bootstrap/issues/24251
         if (this._config.boundary !== 'scrollParent') {
           $(parent).addClass(ClassName.POSITION_STATIC)
         }
-        this._popper = new Popper(element, this._menu, this._getPopperConfig())
+        this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
       }
 
       // If this is a touch-enabled device we add extra
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index b2e588677c..27a888f040 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -161,8 +161,34 @@
             </div>
           </div>
         </div>
+      </div>
 
+      <div class="row">
+        <div class="col-sm-12 mt-4">
+          <div class="btn-group dropdown">
+            <button type="button" class="btn btn-secondary" data-offset="10,20">Dropdown offset</button>
+            <div class="dropdown-menu">
+              <a class="dropdown-item" href="#">Action</a>
+              <a class="dropdown-item" href="#">Another action</a>
+              <a class="dropdown-item" href="#">Something else here</a>
+            </div>
+          </div>
+        </div>
+        <div class="col-sm-12 mt-4">
+          <div class="btn-group dropdown">
+            <button type="button" class="btn btn-secondary">Dropdown reference</button>
+            <button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-reference="parent">
+              <span class="sr-only">Dropdown split</span>
+            </button>
+            <div class="dropdown-menu">
+              <a class="dropdown-item" href="#">Action</a>
+              <a class="dropdown-item" href="#">Another action</a>
+              <a class="dropdown-item" href="#">Something else here</a>
+            </div>
+          </div>
+        </div>
       </div>
+
     </div>
 
     <script src="../../../assets/js/vendor/jquery-slim.min.js"></script>
-- 
GitLab