]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Collapse supports multi-target thanks to @vanduynslagerp (#22713)
authorJohann-S <johann.servoire@gmail.com>
Wed, 14 Jun 2017 11:21:49 +0000 (13:21 +0200)
committerGitHub <noreply@github.com>
Wed, 14 Jun 2017 11:21:49 +0000 (13:21 +0200)
docs/4.0/components/collapse.md
js/src/collapse.js
js/tests/unit/collapse.js

index cbdc50bb72d957f5e48b109cbb29c3f221d2c7b4..e1d3e3b644426d574e7fec5f58db53729b8ecbc9 100644 (file)
@@ -32,6 +32,35 @@ You can use a link with the `href` attribute, or a button with the `data-target`
 </div>
 {% endexample %}
 
+## Multiple triggers / targets
+
+A `<button>` or `<a>` can show and hide multiple elements by referencing them with a JQuery selector in its `href` or `data-target` attribute.
+Multiple `<button>` or `<a>` can  show and hide an element if they each reference it with their `href` or `data-target` attribute
+
+{% example html %}
+<p>
+  <a class="btn btn-primary" data-toggle="collapse" href="#multiCollapseExample1" aria-expanded="false" aria-controls="multiCollapseExample1">Toggle first element</a>
+  <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#multiCollapseExample2" aria-expanded="false" aria-controls="multiCollapseExample1">Toggle second element</button>
+  <button class="btn btn-primary" type="button" data-toggle="collapse" data-target=".multi-collapse" aria-expanded="false" aria-controls="multiCollapseExample1 multiCollapseExample2">Toggle both elements</button>
+</p>
+<div class="row">
+  <div class="col">
+    <div class="collapse multi-collapse" id="multiCollapseExample1">
+      <div class="card card-block">
+        Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident.
+      </div>
+    </div>
+  </div>
+  <div class="col">
+    <div class="collapse multi-collapse" id="multiCollapseExample2">
+      <div class="card card-block">
+        Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident.
+      </div>
+    </div>
+  </div>
+</div>
+{% endexample %}
+
 ## Accordion example
 
 Using the [card]({{ site.baseurl }}/docs/{{ site.docs_version }}/components/card/) component, you can extend the default collapse behavior to create an accordion.
@@ -129,7 +158,7 @@ These classes can be found in `_transitions.scss`.
 
 ### Via data attributes
 
-Just add `data-toggle="collapse"` and a `data-target` to the element to automatically assign control of a collapsible element. The `data-target` attribute accepts a CSS selector to apply the collapse to. Be sure to add the class `collapse` to the collapsible element. If you'd like it to default open, add the additional class `show`.
+Just add `data-toggle="collapse"` and a `data-target` to the element to automatically assign control of one or more collapsible elements. The `data-target` attribute accepts a CSS selector to apply the collapse to. Be sure to add the class `collapse` to the collapsible element. If you'd like it to default open, add the additional class `show`.
 
 To add accordion-like group management to a collapsible area, add the data attribute `data-parent="#selector"`. Refer to the demo to see this in action.
 
index bf1c738f56c80815e5fd655194441a2045ef3503..78ed32906bc392448aaf0aa34e7cb7c6b5b485cb 100644 (file)
@@ -77,6 +77,14 @@ const Collapse = (($) => {
         `[data-toggle="collapse"][href="#${element.id}"],` +
         `[data-toggle="collapse"][data-target="#${element.id}"]`
       ))
+      const tabToggles = $(Selector.DATA_TOGGLE)
+      for (let i = 0; i < tabToggles.length; i++) {
+        const elem = tabToggles[i]
+        const selector = Util.getSelectorFromElement(elem)
+        if (selector !== null && $(selector).filter(element).length > 0) {
+          this._triggerArray.push(elem)
+        }
+      }
 
       this._parent = this._config.parent ? this._getParent() : null
 
@@ -215,9 +223,17 @@ const Collapse = (($) => {
         .removeClass(ClassName.SHOW)
 
       if (this._triggerArray.length) {
-        $(this._triggerArray)
-          .addClass(ClassName.COLLAPSED)
-          .attr('aria-expanded', false)
+        for (let i = 0; i < this._triggerArray.length; i++) {
+          const trigger = this._triggerArray[i]
+          const selector = Util.getSelectorFromElement(trigger)
+          if (selector !== null) {
+            const $elem = $(selector)
+            if (!$elem.hasClass(ClassName.SHOW)) {
+              $(trigger).addClass(ClassName.COLLAPSED)
+                   .attr('aria-expanded', false)
+            }
+          }
+        }
       }
 
       this.setTransitioning(true)
@@ -349,11 +365,14 @@ const Collapse = (($) => {
       event.preventDefault()
     }
 
-    const target = Collapse._getTargetFromElement(this)
-    const data   = $(target).data(DATA_KEY)
-    const config = data ? 'toggle' : $(this).data()
-
-    Collapse._jQueryInterface.call($(target), config)
+    const $trigger = $(this)
+    const selector = Util.getSelectorFromElement(this)
+    $(selector).each(function () {
+      const $target = $(this)
+      const data    = $target.data(DATA_KEY)
+      const config  = data ? 'toggle' : $trigger.data()
+      Collapse._jQueryInterface.call($target, config)
+    })
   })
 
 
index 2b9d0e58fc0b21c68a3f7e94e35643baedcc613f..0e9e8b6a73dd6b43852f51d62740a1443b303a7a 100644 (file)
@@ -52,8 +52,28 @@ $(function () {
     assert.ok(!/height/i.test($el.attr('style')), 'has height reset')
   })
 
+
+  QUnit.test('should show multiple collapsed elements', function (assert) {
+    assert.expect(4)
+    var done = assert.async()
+    var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href=".multi"/>').appendTo('#qunit-fixture')
+    var $el = $('<div class="collapse multi"/>').appendTo('#qunit-fixture')
+    var $el2 = $('<div class="collapse multi"/>').appendTo('#qunit-fixture')
+    $el.one('shown.bs.collapse', function () {
+      assert.ok($el.hasClass('show'), 'has class "show"')
+      assert.ok(!/height/i.test($el.attr('style')), 'has height reset')
+    })
+    $el2.one('shown.bs.collapse', function () {
+      assert.ok($el2.hasClass('show'), 'has class "show"')
+      assert.ok(!/height/i.test($el2.attr('style')), 'has height reset')
+      done()
+    })
+    $target.trigger('click')
+  })
+
   QUnit.test('should collapse only the first collapse', function (assert) {
     assert.expect(2)
+    var done = assert.async()
     var html = [
       '<div class="panel-group" id="accordion1">',
       '<div class="panel">',
@@ -69,10 +89,11 @@ $(function () {
     $(html).appendTo('#qunit-fixture')
     var $el1 = $('#collapse1')
     var $el2 = $('#collapse2')
-    $el1.bootstrapCollapse('show')
-
-    assert.ok($el1.hasClass('show'))
-    assert.ok($el2.hasClass('show'))
+    $el1.one('shown.bs.collapse', function () {
+      assert.ok($el1.hasClass('show'))
+      assert.ok($el2.hasClass('show'))
+      done()
+    }).bootstrapCollapse('show')
   })
 
   QUnit.test('should hide a collapsed element', function (assert) {
@@ -588,4 +609,68 @@ $(function () {
 
     $target.trigger($.Event('click'))
   })
+
+  QUnit.test('should add "collapsed" class to triggers only when all the targeted collapse are hidden', function (assert) {
+    assert.expect(9)
+    var done = assert.async()
+
+    var $trigger1 = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
+    var $trigger2 = $('<a role="button" data-toggle="collapse" href="#test2"/>').appendTo('#qunit-fixture')
+    var $trigger3 = $('<a role="button" data-toggle="collapse" href=".multi"/>').appendTo('#qunit-fixture')
+
+    var $target1 = $('<div id="test1" class="multi"/>').appendTo('#qunit-fixture')
+    var $target2 = $('<div id="test2" class="multi"/>').appendTo('#qunit-fixture')
+
+    $target2.one('shown.bs.collapse', function () {
+      assert.ok(!$trigger1.hasClass('collapsed'), 'trigger1 does not have collapsed class')
+      assert.ok(!$trigger2.hasClass('collapsed'), 'trigger2 does not have collapsed class')
+      assert.ok(!$trigger3.hasClass('collapsed'), 'trigger3 does not have collapsed class')
+      $target2.one('hidden.bs.collapse', function () {
+        assert.ok(!$trigger1.hasClass('collapsed'), 'trigger1 does not have collapsed class')
+        assert.ok($trigger2.hasClass('collapsed'), 'trigger2 has collapsed class')
+        assert.ok(!$trigger3.hasClass('collapsed'), 'trigger3 does not have collapsed class')
+        $target1.one('hidden.bs.collapse', function () {
+          assert.ok($trigger1.hasClass('collapsed'), 'trigger1 has collapsed class')
+          assert.ok($trigger2.hasClass('collapsed'), 'trigger2 has collapsed class')
+          assert.ok($trigger3.hasClass('collapsed'), 'trigger3 has collapsed class')
+          done()
+        })
+        $trigger1.trigger('click')
+      })
+      $trigger2.trigger('click')
+    })
+    $trigger3.trigger('click')
+  })
+
+  QUnit.test('should set aria-expanded="true" to triggers targetting shown collaspe and aria-expanded="false" only when all the targeted collapses are shown', function (assert) {
+    assert.expect(9)
+    var done = assert.async()
+
+    var $trigger1 = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
+    var $trigger2 = $('<a role="button" data-toggle="collapse" href="#test2"/>').appendTo('#qunit-fixture')
+    var $trigger3 = $('<a role="button" data-toggle="collapse" href=".multi"/>').appendTo('#qunit-fixture')
+
+    var $target1 = $('<div id="test1" class="multi collapse"/>').appendTo('#qunit-fixture')
+    var $target2 = $('<div id="test2" class="multi collapse"/>').appendTo('#qunit-fixture')
+
+    $target2.one('shown.bs.collapse', function () {
+      assert.strictEqual($trigger1.attr('aria-expanded'), 'true', 'aria-expanded on trigger1 is "true"')
+      assert.strictEqual($trigger2.attr('aria-expanded'), 'true', 'aria-expanded on trigger2 is "true"')
+      assert.strictEqual($trigger3.attr('aria-expanded'), 'true', 'aria-expanded on trigger3 is "true"')
+      $target2.one('hidden.bs.collapse', function () {
+        assert.strictEqual($trigger1.attr('aria-expanded'), 'true', 'aria-expanded on trigger1 is "true"')
+        assert.strictEqual($trigger2.attr('aria-expanded'), 'false', 'aria-expanded on trigger2 is "false"')
+        assert.strictEqual($trigger3.attr('aria-expanded'), 'true', 'aria-expanded on trigger3 is "true"')
+        $target1.one('hidden.bs.collapse', function () {
+          assert.strictEqual($trigger1.attr('aria-expanded'), 'false', 'aria-expanded on trigger1 is "fasle"')
+          assert.strictEqual($trigger2.attr('aria-expanded'), 'false', 'aria-expanded on trigger2 is "false"')
+          assert.strictEqual($trigger3.attr('aria-expanded'), 'false', 'aria-expanded on trigger3 is "false"')
+          done()
+        })
+        $trigger1.trigger('click')
+      })
+      $trigger2.trigger('click')
+    })
+    $trigger3.trigger('click')
+  })
 })